Go back to index | PHP CodeBrowser

phpseclib/File/X509.php
  1.    1  <?php
  2.    2 
  3.    3 /**
  4.    4  * Pure-PHP X.509 Parser
  5.    5  *
  6.    6  * PHP versions 4 and 5
  7.    7  *
  8.    8  * Encode and decode X.509 certificates.
  9.    9  *
  10.   10  * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
  11.   11  * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
  12.   12  *
  13.   13  * Note that loading an X.509 certificate and resaving it may invalidate the signature.  The reason being that the signature is based on a
  14.   14  * portion of the certificate that contains optional parameters with default values.  ie. if the parameter isn't there the default value is
  15.   15  * used.  Problem is, if the parameter is there and it just so happens to have the default value there are two ways that that parameter can
  16.   16  * be encoded.  It can be encoded explicitly or left out all together.  This would effect the signature value and thus may invalidate the
  17.   17  * the certificate all together unless the certificate is re-signed.
  18.   18  *
  19.   19  * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  20.   20  * of this software and associated documentation files (the "Software"), to deal
  21.   21  * in the Software without restriction, including without limitation the rights
  22.   22  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  23.   23  * copies of the Software, and to permit persons to whom the Software is
  24.   24  * furnished to do so, subject to the following conditions:
  25.   25  *
  26.   26  * The above copyright notice and this permission notice shall be included in
  27.   27  * all copies or substantial portions of the Software.
  28.   28  *
  29.   29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  30.   30  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  31.   31  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  32.   32  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  33.   33  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  34.   34  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  35.   35  * THE SOFTWARE.
  36.   36  *
  37.   37  * @category  File
  38.   38  * @package   File_X509
  39.   39  * @author    Jim Wigginton <terrafrost@php.net>
  40.   40  * @copyright 2012 Jim Wigginton
  41.   41  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  42.   42  * @link      http://phpseclib.sourceforge.net
  43.   43  */
  44.   44 
  45.   45 /**
  46.   46  * Include File_ASN1
  47.   47  */
  48.   48 if (!class_exists('File_ASN1')) {
  49.   49     include_once 'ASN1.php';
  50.   50 }
  51.   51 
  52.   52 /**
  53.   53  * Flag to only accept signatures signed by certificate authorities
  54.   54  *
  55.   55  * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs
  56.   56  *
  57.   57  * @access public
  58.   58  */
  59.   59 define('FILE_X509_VALIDATE_SIGNATURE_BY_CA'1);
  60.   60 
  61.   61 /**#@+
  62.   62  * @access public
  63.   63  * @see self::getDN()
  64.   64  */
  65.   65 /**
  66.   66  * Return internal array representation
  67.   67  */
  68.   68 define('FILE_X509_DN_ARRAY'0);
  69.   69 /**
  70.   70  * Return string
  71.   71  */
  72.   72 define('FILE_X509_DN_STRING'1);
  73.   73 /**
  74.   74  * Return ASN.1 name string
  75.   75  */
  76.   76 define('FILE_X509_DN_ASN1'2);
  77.   77 /**
  78.   78  * Return OpenSSL compatible array
  79.   79  */
  80.   80 define('FILE_X509_DN_OPENSSL'3);
  81.   81 /**
  82.   82  * Return canonical ASN.1 RDNs string
  83.   83  */
  84.   84 define('FILE_X509_DN_CANON'4);
  85.   85 /**
  86.   86  * Return name hash for file indexing
  87.   87  */
  88.   88 define('FILE_X509_DN_HASH'5);
  89.   89 /**#@-*/
  90.   90 
  91.   91 /**#@+
  92.   92  * @access public
  93.   93  * @see self::saveX509()
  94.   94  * @see self::saveCSR()
  95.   95  * @see self::saveCRL()
  96.   96  */
  97.   97 /**
  98.   98  * Save as PEM
  99.   99  *
  100.  100  * ie. a base64-encoded PEM with a header and a footer
  101.  101  */
  102.  102 define('FILE_X509_FORMAT_PEM'0);
  103.  103 /**
  104.  104  * Save as DER
  105.  105  */
  106.  106 define('FILE_X509_FORMAT_DER'1);
  107.  107 /**
  108.  108  * Save as a SPKAC
  109.  109  *
  110.  110  * Only works on CSRs. Not currently supported.
  111.  111  */
  112.  112 define('FILE_X509_FORMAT_SPKAC'2);
  113.  113 /**
  114.  114  * Auto-detect the format
  115.  115  *
  116.  116  * Used only by the load*() functions
  117.  117  */
  118.  118 define('FILE_X509_FORMAT_AUTO_DETECT'3);
  119.  119 /**#@-*/
  120.  120 
  121.  121 /**
  122.  122  * Attribute value disposition.
  123.  123  * If disposition is >= 0, this is the index of the target value.
  124.  124  */
  125.  125 define('FILE_X509_ATTR_ALL', -1); // All attribute values (array).
  126.  126 define('FILE_X509_ATTR_APPEND', -2); // Add a value.
  127.  127 define('FILE_X509_ATTR_REPLACE', -3); // Clear first, then add a value.
  128.  128 
  129.  129 /**
  130.  130  * Pure-PHP X.509 Parser
  131.  131  *
  132.  132  * @package File_X509
  133.  133  * @author  Jim Wigginton <terrafrost@php.net>
  134.  134  * @access  public
  135.  135  */
  136.  136 class File_X509
  137.  137 {
  138.  138     /**
  139.  139      * ASN.1 syntax for X.509 certificates
  140.  140      *
  141.  141      * @var array
  142.  142      * @access private
  143.  143      */
  144.  144     var $Certificate;
  145.  145 
  146.  146     /**#@+
  147.  147      * ASN.1 syntax for various extensions
  148.  148      *
  149.  149      * @access private
  150.  150      */
  151.  151     var $DirectoryString;
  152.  152     var $PKCS9String;
  153.  153     var $AttributeValue;
  154.  154     var $Extensions;
  155.  155     var $KeyUsage;
  156.  156     var $ExtKeyUsageSyntax;
  157.  157     var $BasicConstraints;
  158.  158     var $KeyIdentifier;
  159.  159     var $CRLDistributionPoints;
  160.  160     var $AuthorityKeyIdentifier;
  161.  161     var $CertificatePolicies;
  162.  162     var $AuthorityInfoAccessSyntax;
  163.  163     var $SubjectAltName;
  164.  164     var $SubjectDirectoryAttributes;
  165.  165     var $PrivateKeyUsagePeriod;
  166.  166     var $IssuerAltName;
  167.  167     var $PolicyMappings;
  168.  168     var $NameConstraints;
  169.  169 
  170.  170     var $CPSuri;
  171.  171     var $UserNotice;
  172.  172 
  173.  173     var $netscape_cert_type;
  174.  174     var $netscape_comment;
  175.  175     var $netscape_ca_policy_url;
  176.  176 
  177.  177     var $Name;
  178.  178     var $RelativeDistinguishedName;
  179.  179     var $CRLNumber;
  180.  180     var $CRLReason;
  181.  181     var $IssuingDistributionPoint;
  182.  182     var $InvalidityDate;
  183.  183     var $CertificateIssuer;
  184.  184     var $HoldInstructionCode;
  185.  185     var $SignedPublicKeyAndChallenge;
  186.  186     /**#@-*/
  187.  187 
  188.  188     /**#@+
  189.  189      * ASN.1 syntax for various DN attributes
  190.  190      *
  191.  191      * @access private
  192.  192      */
  193.  193     var $PostalAddress;
  194.  194     /**#@-*/
  195.  195 
  196.  196     /**
  197.  197      * ASN.1 syntax for Certificate Signing Requests (RFC2986)
  198.  198      *
  199.  199      * @var array
  200.  200      * @access private
  201.  201      */
  202.  202     var $CertificationRequest;
  203.  203 
  204.  204     /**
  205.  205      * ASN.1 syntax for Certificate Revocation Lists (RFC5280)
  206.  206      *
  207.  207      * @var array
  208.  208      * @access private
  209.  209      */
  210.  210     var $CertificateList;
  211.  211 
  212.  212     /**
  213.  213      * Distinguished Name
  214.  214      *
  215.  215      * @var array
  216.  216      * @access private
  217.  217      */
  218.  218     var $dn;
  219.  219 
  220.  220     /**
  221.  221      * Public key
  222.  222      *
  223.  223      * @var string
  224.  224      * @access private
  225.  225      */
  226.  226     var $publicKey;
  227.  227 
  228.  228     /**
  229.  229      * Private key
  230.  230      *
  231.  231      * @var string
  232.  232      * @access private
  233.  233      */
  234.  234     var $privateKey;
  235.  235 
  236.  236     /**
  237.  237      * Object identifiers for X.509 certificates
  238.  238      *
  239.  239      * @var array
  240.  240      * @access private
  241.  241      * @link http://en.wikipedia.org/wiki/Object_identifier
  242.  242      */
  243.  243     var $oids;
  244.  244 
  245.  245     /**
  246.  246      * The certificate authorities
  247.  247      *
  248.  248      * @var array
  249.  249      * @access private
  250.  250      */
  251.  251     var $CAs;
  252.  252 
  253.  253     /**
  254.  254      * The currently loaded certificate
  255.  255      *
  256.  256      * @var array
  257.  257      * @access private
  258.  258      */
  259.  259     var $currentCert;
  260.  260 
  261.  261     /**
  262.  262      * The signature subject
  263.  263      *
  264.  264      * There's no guarantee File_X509 is going to re-encode an X.509 cert in the same way it was originally
  265.  265      * encoded so we take save the portion of the original cert that the signature would have made for.
  266.  266      *
  267.  267      * @var string
  268.  268      * @access private
  269.  269      */
  270.  270     var $signatureSubject;
  271.  271 
  272.  272     /**
  273.  273      * Certificate Start Date
  274.  274      *
  275.  275      * @var string
  276.  276      * @access private
  277.  277      */
  278.  278     var $startDate;
  279.  279 
  280.  280     /**
  281.  281      * Certificate End Date
  282.  282      *
  283.  283      * @var string
  284.  284      * @access private
  285.  285      */
  286.  286     var $endDate;
  287.  287 
  288.  288     /**
  289.  289      * Serial Number
  290.  290      *
  291.  291      * @var string
  292.  292      * @access private
  293.  293      */
  294.  294     var $serialNumber;
  295.  295 
  296.  296     /**
  297.  297      * Key Identifier
  298.  298      *
  299.  299      * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and
  300.  300      * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}.
  301.  301      *
  302.  302      * @var string
  303.  303      * @access private
  304.  304      */
  305.  305     var $currentKeyIdentifier;
  306.  306 
  307.  307     /**
  308.  308      * CA Flag
  309.  309      *
  310.  310      * @var bool
  311.  311      * @access private
  312.  312      */
  313.  313     var $caFlag false;
  314.  314 
  315.  315     /**
  316.  316      * SPKAC Challenge
  317.  317      *
  318.  318      * @var string
  319.  319      * @access private
  320.  320      */
  321.  321     var $challenge;
  322.  322 
  323.  323     /**
  324.  324      * Default Constructor.
  325.  325      *
  326.  326      * @return File_X509
  327.  327      * @access public
  328.  328      */
  329.  329     function __construct()
  330.  330     {
  331.  331         if (!class_exists('Math_BigInteger')) {
  332.  332             include_once 'Math/BigInteger.php';
  333.  333         }
  334.  334 
  335.  335         // Explicitly Tagged Module, 1988 Syntax
  336.  336         // http://tools.ietf.org/html/rfc5280#appendix-A.1
  337.  337 
  338.  338         $this->DirectoryString = array(
  339.  339             'type'     => FILE_ASN1_TYPE_CHOICE,
  340.  340             'children' => array(
  341.  341                 'teletexString'   => array('type' => FILE_ASN1_TYPE_TELETEX_STRING),
  342.  342                 'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
  343.  343                 'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING),
  344.  344                 'utf8String'      => array('type' => FILE_ASN1_TYPE_UTF8_STRING),
  345.  345                 'bmpString'       => array('type' => FILE_ASN1_TYPE_BMP_STRING)
  346.  346             )
  347.  347         );
  348.  348 
  349.  349         $this->PKCS9String = array(
  350.  350             'type'     => FILE_ASN1_TYPE_CHOICE,
  351.  351             'children' => array(
  352.  352                 'ia5String'       => array('type' => FILE_ASN1_TYPE_IA5_STRING),
  353.  353                 'directoryString' => $this->DirectoryString
  354.  354             )
  355.  355         );
  356.  356 
  357.  357         $this->AttributeValue = array('type' => FILE_ASN1_TYPE_ANY);
  358.  358 
  359.  359         $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  360.  360 
  361.  361         $AttributeTypeAndValue = array(
  362.  362             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  363.  363             'children' => array(
  364.  364                 'type' => $AttributeType,
  365.  365                 'value'=> $this->AttributeValue
  366.  366             )
  367.  367         );
  368.  368 
  369.  369         /*
  370.  370         In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
  371.  371         but they can be useful at times when either there is no unique attribute in the entry or you
  372.  372         want to ensure that the entry's DN contains some useful identifying information.
  373.  373 
  374.  374         - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
  375.  375         */
  376.  376         $this->RelativeDistinguishedName = array(
  377.  377             'type'     => FILE_ASN1_TYPE_SET,
  378.  378             'min'      => 1,
  379.  379             'max'      => -1,
  380.  380             'children' => $AttributeTypeAndValue
  381.  381         );
  382.  382 
  383.  383         // http://tools.ietf.org/html/rfc5280#section-4.1.2.4
  384.  384         $RDNSequence = array(
  385.  385             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  386.  386             // RDNSequence does not define a min or a max, which means it doesn't have one
  387.  387             'min'      => 0,
  388.  388             'max'      => -1,
  389.  389             'children' => $this->RelativeDistinguishedName
  390.  390         );
  391.  391 
  392.  392         $this->Name = array(
  393.  393             'type'     => FILE_ASN1_TYPE_CHOICE,
  394.  394             'children' => array(
  395.  395                 'rdnSequence' => $RDNSequence
  396.  396             )
  397.  397         );
  398.  398 
  399.  399         // http://tools.ietf.org/html/rfc5280#section-4.1.1.2
  400.  400         $AlgorithmIdentifier = array(
  401.  401             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  402.  402             'children' => array(
  403.  403                 'algorithm'  => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  404.  404                 'parameters' => array(
  405.  405                                     'type'     => FILE_ASN1_TYPE_ANY,
  406.  406                                     'optional' => true
  407.  407                                 )
  408.  408             )
  409.  409         );
  410.  410 
  411.  411         /*
  412.  412            A certificate using system MUST reject the certificate if it encounters
  413.  413            a critical extension it does not recognize; however, a non-critical
  414.  414            extension may be ignored if it is not recognized.
  415.  415 
  416.  416            http://tools.ietf.org/html/rfc5280#section-4.2
  417.  417         */
  418.  418         $Extension = array(
  419.  419             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  420.  420             'children' => array(
  421.  421                 'extnId'   => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  422.  422                 'critical' => array(
  423.  423                                   'type'     => FILE_ASN1_TYPE_BOOLEAN,
  424.  424                                   'optional' => true,
  425.  425                                   'default'  => false
  426.  426                               ),
  427.  427                 'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING)
  428.  428             )
  429.  429         );
  430.  430 
  431.  431         $this->Extensions = array(
  432.  432             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  433.  433             'min'      => 1,
  434.  434             // technically, it's MAX, but we'll assume anything < 0 is MAX
  435.  435             'max'      => -1,
  436.  436             // if 'children' isn't an array then 'min' and 'max' must be defined
  437.  437             'children' => $Extension
  438.  438         );
  439.  439 
  440.  440         $SubjectPublicKeyInfo = array(
  441.  441             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  442.  442             'children' => array(
  443.  443                 'algorithm'        => $AlgorithmIdentifier,
  444.  444                 'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  445.  445             )
  446.  446         );
  447.  447 
  448.  448         $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING);
  449.  449 
  450.  450         $Time = array(
  451.  451             'type'     => FILE_ASN1_TYPE_CHOICE,
  452.  452             'children' => array(
  453.  453                 'utcTime'     => array('type' => FILE_ASN1_TYPE_UTC_TIME),
  454.  454                 'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
  455.  455             )
  456.  456         );
  457.  457 
  458.  458         // http://tools.ietf.org/html/rfc5280#section-4.1.2.5
  459.  459         $Validity = array(
  460.  460             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  461.  461             'children' => array(
  462.  462                 'notBefore' => $Time,
  463.  463                 'notAfter'  => $Time
  464.  464             )
  465.  465         );
  466.  466 
  467.  467         $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER);
  468.  468 
  469.  469         $Version = array(
  470.  470             'type'    => FILE_ASN1_TYPE_INTEGER,
  471.  471             'mapping' => array('v1''v2''v3')
  472.  472         );
  473.  473 
  474.  474         // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
  475.  475         $TBSCertificate = array(
  476.  476             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  477.  477             'children' => array(
  478.  478                 // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
  479.  479                 // reenforce that fact
  480.  480                 'version'             => array(
  481.  481                                              'constant' => 0,
  482.  482                                              'optional' => true,
  483.  483                                              'explicit' => true,
  484.  484                                              'default'  => 'v1'
  485.  485                                          ) + $Version,
  486.  486                 'serialNumber'         => $CertificateSerialNumber,
  487.  487                 'signature'            => $AlgorithmIdentifier,
  488.  488                 'issuer'               => $this->Name,
  489.  489                 'validity'             => $Validity,
  490.  490                 'subject'              => $this->Name,
  491.  491                 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,
  492.  492                 // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
  493.  493                 'issuerUniqueID'       => array(
  494.  494                                                'constant' => 1,
  495.  495                                                'optional' => true,
  496.  496                                                'implicit' => true
  497.  497                                            ) + $UniqueIdentifier,
  498.  498                 'subjectUniqueID'       => array(
  499.  499                                                'constant' => 2,
  500.  500                                                'optional' => true,
  501.  501                                                'implicit' => true
  502.  502                                            ) + $UniqueIdentifier,
  503.  503                 // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
  504.  504                 // it's not IMPLICIT, it's EXPLICIT
  505.  505                 'extensions'            => array(
  506.  506                                                'constant' => 3,
  507.  507                                                'optional' => true,
  508.  508                                                'explicit' => true
  509.  509                                            ) + $this->Extensions
  510.  510             )
  511.  511         );
  512.  512 
  513.  513         $this->Certificate = array(
  514.  514             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  515.  515             'children' => array(
  516.  516                  'tbsCertificate'     => $TBSCertificate,
  517.  517                  'signatureAlgorithm' => $AlgorithmIdentifier,
  518.  518                  'signature'          => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  519.  519             )
  520.  520         );
  521.  521 
  522.  522         $this->KeyUsage = array(
  523.  523             'type'    => FILE_ASN1_TYPE_BIT_STRING,
  524.  524             'mapping' => array(
  525.  525                 'digitalSignature',
  526.  526                 'nonRepudiation',
  527.  527                 'keyEncipherment',
  528.  528                 'dataEncipherment',
  529.  529                 'keyAgreement',
  530.  530                 'keyCertSign',
  531.  531                 'cRLSign',
  532.  532                 'encipherOnly',
  533.  533                 'decipherOnly'
  534.  534             )
  535.  535         );
  536.  536 
  537.  537         $this->BasicConstraints = array(
  538.  538             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  539.  539             'children' => array(
  540.  540                 'cA'                => array(
  541.  541                                                  'type'     => FILE_ASN1_TYPE_BOOLEAN,
  542.  542                                                  'optional' => true,
  543.  543                                                  'default'  => false
  544.  544                                        ),
  545.  545                 'pathLenConstraint' => array(
  546.  546                                                  'type' => FILE_ASN1_TYPE_INTEGER,
  547.  547                                                  'optional' => true
  548.  548                                        )
  549.  549             )
  550.  550         );
  551.  551 
  552.  552         $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING);
  553.  553 
  554.  554         $OrganizationalUnitNames = array(
  555.  555             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  556.  556             'min'      => 1,
  557.  557             'max'      => 4// ub-organizational-units
  558.  558             'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  559.  559         );
  560.  560 
  561.  561         $PersonalName = array(
  562.  562             'type'     => FILE_ASN1_TYPE_SET,
  563.  563             'children' => array(
  564.  564                 'surname'              => array(
  565.  565                                            'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  566.  566                                            'constant' => 0,
  567.  567                                            'optional' => true,
  568.  568                                            'implicit' => true
  569.  569                                          ),
  570.  570                 'given-name'           => array(
  571.  571                                            'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  572.  572                                            'constant' => 1,
  573.  573                                            'optional' => true,
  574.  574                                            'implicit' => true
  575.  575                                          ),
  576.  576                 'initials'             => array(
  577.  577                                            'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  578.  578                                            'constant' => 2,
  579.  579                                            'optional' => true,
  580.  580                                            'implicit' => true
  581.  581                                          ),
  582.  582                 'generation-qualifier' => array(
  583.  583                                            'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  584.  584                                            'constant' => 3,
  585.  585                                            'optional' => true,
  586.  586                                            'implicit' => true
  587.  587                                          )
  588.  588             )
  589.  589         );
  590.  590 
  591.  591         $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
  592.  592 
  593.  593         $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
  594.  594 
  595.  595         $PrivateDomainName = array(
  596.  596             'type'     => FILE_ASN1_TYPE_CHOICE,
  597.  597             'children' => array(
  598.  598                 'numeric'   => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
  599.  599                 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  600.  600             )
  601.  601         );
  602.  602 
  603.  603         $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);
  604.  604 
  605.  605         $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);
  606.  606 
  607.  607         $AdministrationDomainName = array(
  608.  608             'type'     => FILE_ASN1_TYPE_CHOICE,
  609.  609             // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
  610.  610             // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
  611.  611             'class'    => FILE_ASN1_CLASS_APPLICATION,
  612.  612             'cast'     => 2,
  613.  613             'children' => array(
  614.  614                 'numeric'   => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
  615.  615                 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  616.  616             )
  617.  617         );
  618.  618 
  619.  619         $CountryName = array(
  620.  620             'type'     => FILE_ASN1_TYPE_CHOICE,
  621.  621             // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
  622.  622             // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
  623.  623             'class'    => FILE_ASN1_CLASS_APPLICATION,
  624.  624             'cast'     => 1,
  625.  625             'children' => array(
  626.  626                 'x121-dcc-code'        => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
  627.  627                 'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  628.  628             )
  629.  629         );
  630.  630 
  631.  631         $AnotherName = array(
  632.  632             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  633.  633             'children' => array(
  634.  634                  'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  635.  635                  'value'   => array(
  636.  636                                   'type' => FILE_ASN1_TYPE_ANY,
  637.  637                                   'constant' => 0,
  638.  638                                   'optional' => true,
  639.  639                                   'explicit' => true
  640.  640                               )
  641.  641             )
  642.  642         );
  643.  643 
  644.  644         $ExtensionAttribute = array(
  645.  645             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  646.  646             'children' => array(
  647.  647                  'extension-attribute-type'  => array(
  648.  648                                                     'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
  649.  649                                                     'constant' => 0,
  650.  650                                                     'optional' => true,
  651.  651                                                     'implicit' => true
  652.  652                                                 ),
  653.  653                  'extension-attribute-value' => array(
  654.  654                                                     'type' => FILE_ASN1_TYPE_ANY,
  655.  655                                                     'constant' => 1,
  656.  656                                                     'optional' => true,
  657.  657                                                     'explicit' => true
  658.  658                                                 )
  659.  659             )
  660.  660         );
  661.  661 
  662.  662         $ExtensionAttributes = array(
  663.  663             'type'     => FILE_ASN1_TYPE_SET,
  664.  664             'min'      => 1,
  665.  665             'max'      => 256// ub-extension-attributes
  666.  666             'children' => $ExtensionAttribute
  667.  667         );
  668.  668 
  669.  669         $BuiltInDomainDefinedAttribute = array(
  670.  670             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  671.  671             'children' => array(
  672.  672                  'type'  => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
  673.  673                  'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
  674.  674             )
  675.  675         );
  676.  676 
  677.  677         $BuiltInDomainDefinedAttributes = array(
  678.  678             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  679.  679             'min'      => 1,
  680.  680             'max'      => 4// ub-domain-defined-attributes
  681.  681             'children' => $BuiltInDomainDefinedAttribute
  682.  682         );
  683.  683 
  684.  684         $BuiltInStandardAttributes =  array(
  685.  685             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  686.  686             'children' => array(
  687.  687                 'country-name'               => array('optional' => true) + $CountryName,
  688.  688                 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
  689.  689                 'network-address'            => array(
  690.  690                                                  'constant' => 0,
  691.  691                                                  'optional' => true,
  692.  692                                                  'implicit' => true
  693.  693                                                ) + $NetworkAddress,
  694.  694                 'terminal-identifier'        => array(
  695.  695                                                  'constant' => 1,
  696.  696                                                  'optional' => true,
  697.  697                                                  'implicit' => true
  698.  698                                                ) + $TerminalIdentifier,
  699.  699                 'private-domain-name'        => array(
  700.  700                                                  'constant' => 2,
  701.  701                                                  'optional' => true,
  702.  702                                                  'explicit' => true
  703.  703                                                ) + $PrivateDomainName,
  704.  704                 'organization-name'          => array(
  705.  705                                                  'constant' => 3,
  706.  706                                                  'optional' => true,
  707.  707                                                  'implicit' => true
  708.  708                                                ) + $OrganizationName,
  709.  709                 'numeric-user-identifier'    => array(
  710.  710                                                  'constant' => 4,
  711.  711                                                  'optional' => true,
  712.  712                                                  'implicit' => true
  713.  713                                                ) + $NumericUserIdentifier,
  714.  714                 'personal-name'              => array(
  715.  715                                                  'constant' => 5,
  716.  716                                                  'optional' => true,
  717.  717                                                  'implicit' => true
  718.  718                                                ) + $PersonalName,
  719.  719                 'organizational-unit-names'  => array(
  720.  720                                                  'constant' => 6,
  721.  721                                                  'optional' => true,
  722.  722                                                  'implicit' => true
  723.  723                                                ) + $OrganizationalUnitNames
  724.  724             )
  725.  725         );
  726.  726 
  727.  727         $ORAddress = array(
  728.  728             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  729.  729             'children' => array(
  730.  730                  'built-in-standard-attributes'       => $BuiltInStandardAttributes,
  731.  731                  'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
  732.  732                  'extension-attributes'               => array('optional' => true) + $ExtensionAttributes
  733.  733             )
  734.  734         );
  735.  735 
  736.  736         $EDIPartyName = array(
  737.  737             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  738.  738             'children' => array(
  739.  739                  'nameAssigner' => array(
  740.  740                                     'constant' => 0,
  741.  741                                     'optional' => true,
  742.  742                                     'implicit' => true
  743.  743                                 ) + $this->DirectoryString,
  744.  744                  // partyName is technically required but File_ASN1 doesn't currently support non-optional constants and
  745.  745                  // setting it to optional gets the job done in any event.
  746.  746                  'partyName'    => array(
  747.  747                                     'constant' => 1,
  748.  748                                     'optional' => true,
  749.  749                                     'implicit' => true
  750.  750                                 ) + $this->DirectoryString
  751.  751             )
  752.  752         );
  753.  753 
  754.  754         $GeneralName = array(
  755.  755             'type'     => FILE_ASN1_TYPE_CHOICE,
  756.  756             'children' => array(
  757.  757                 'otherName'                 => array(
  758.  758                                                  'constant' => 0,
  759.  759                                                  'optional' => true,
  760.  760                                                  'implicit' => true
  761.  761                                                ) + $AnotherName,
  762.  762                 'rfc822Name'                => array(
  763.  763                                                  'type' => FILE_ASN1_TYPE_IA5_STRING,
  764.  764                                                  'constant' => 1,
  765.  765                                                  'optional' => true,
  766.  766                                                  'implicit' => true
  767.  767                                                ),
  768.  768                 'dNSName'                   => array(
  769.  769                                                  'type' => FILE_ASN1_TYPE_IA5_STRING,
  770.  770                                                  'constant' => 2,
  771.  771                                                  'optional' => true,
  772.  772                                                  'implicit' => true
  773.  773                                                ),
  774.  774                 'x400Address'               => array(
  775.  775                                                  'constant' => 3,
  776.  776                                                  'optional' => true,
  777.  777                                                  'implicit' => true
  778.  778                                                ) + $ORAddress,
  779.  779                 'directoryName'             => array(
  780.  780                                                  'constant' => 4,
  781.  781                                                  'optional' => true,
  782.  782                                                  'explicit' => true
  783.  783                                                ) + $this->Name,
  784.  784                 'ediPartyName'              => array(
  785.  785                                                  'constant' => 5,
  786.  786                                                  'optional' => true,
  787.  787                                                  'implicit' => true
  788.  788                                                ) + $EDIPartyName,
  789.  789                 'uniformResourceIdentifier' => array(
  790.  790                                                  'type' => FILE_ASN1_TYPE_IA5_STRING,
  791.  791                                                  'constant' => 6,
  792.  792                                                  'optional' => true,
  793.  793                                                  'implicit' => true
  794.  794                                                ),
  795.  795                 'iPAddress'                 => array(
  796.  796                                                  'type' => FILE_ASN1_TYPE_OCTET_STRING,
  797.  797                                                  'constant' => 7,
  798.  798                                                  'optional' => true,
  799.  799                                                  'implicit' => true
  800.  800                                                ),
  801.  801                 'registeredID'              => array(
  802.  802                                                  'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER,
  803.  803                                                  'constant' => 8,
  804.  804                                                  'optional' => true,
  805.  805                                                  'implicit' => true
  806.  806                                                )
  807.  807             )
  808.  808         );
  809.  809 
  810.  810         $GeneralNames = array(
  811.  811             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  812.  812             'min'      => 1,
  813.  813             'max'      => -1,
  814.  814             'children' => $GeneralName
  815.  815         );
  816.  816 
  817.  817         $this->IssuerAltName $GeneralNames;
  818.  818 
  819.  819         $ReasonFlags = array(
  820.  820             'type'    => FILE_ASN1_TYPE_BIT_STRING,
  821.  821             'mapping' => array(
  822.  822                 'unused',
  823.  823                 'keyCompromise',
  824.  824                 'cACompromise',
  825.  825                 'affiliationChanged',
  826.  826                 'superseded',
  827.  827                 'cessationOfOperation',
  828.  828                 'certificateHold',
  829.  829                 'privilegeWithdrawn',
  830.  830                 'aACompromise'
  831.  831             )
  832.  832         );
  833.  833 
  834.  834         $DistributionPointName = array(
  835.  835             'type'     => FILE_ASN1_TYPE_CHOICE,
  836.  836             'children' => array(
  837.  837                 'fullName'                => array(
  838.  838                                                  'constant' => 0,
  839.  839                                                  'optional' => true,
  840.  840                                                  'implicit' => true
  841.  841                                        ) + $GeneralNames,
  842.  842                 'nameRelativeToCRLIssuer' => array(
  843.  843                                                  'constant' => 1,
  844.  844                                                  'optional' => true,
  845.  845                                                  'implicit' => true
  846.  846                                        ) + $this->RelativeDistinguishedName
  847.  847             )
  848.  848         );
  849.  849 
  850.  850         $DistributionPoint = array(
  851.  851             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  852.  852             'children' => array(
  853.  853                 'distributionPoint' => array(
  854.  854                                                  'constant' => 0,
  855.  855                                                  'optional' => true,
  856.  856                                                  'explicit' => true
  857.  857                                        ) + $DistributionPointName,
  858.  858                 'reasons'           => array(
  859.  859                                                  'constant' => 1,
  860.  860                                                  'optional' => true,
  861.  861                                                  'implicit' => true
  862.  862                                        ) + $ReasonFlags,
  863.  863                 'cRLIssuer'         => array(
  864.  864                                                  'constant' => 2,
  865.  865                                                  'optional' => true,
  866.  866                                                  'implicit' => true
  867.  867                                        ) + $GeneralNames
  868.  868             )
  869.  869         );
  870.  870 
  871.  871         $this->CRLDistributionPoints = array(
  872.  872             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  873.  873             'min'      => 1,
  874.  874             'max'      => -1,
  875.  875             'children' => $DistributionPoint
  876.  876         );
  877.  877 
  878.  878         $this->AuthorityKeyIdentifier = array(
  879.  879             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  880.  880             'children' => array(
  881.  881                 'keyIdentifier'             => array(
  882.  882                                                  'constant' => 0,
  883.  883                                                  'optional' => true,
  884.  884                                                  'implicit' => true
  885.  885                                                ) + $this->KeyIdentifier,
  886.  886                 'authorityCertIssuer'       => array(
  887.  887                                                  'constant' => 1,
  888.  888                                                  'optional' => true,
  889.  889                                                  'implicit' => true
  890.  890                                                ) + $GeneralNames,
  891.  891                 'authorityCertSerialNumber' => array(
  892.  892                                                  'constant' => 2,
  893.  893                                                  'optional' => true,
  894.  894                                                  'implicit' => true
  895.  895                                                ) + $CertificateSerialNumber
  896.  896             )
  897.  897         );
  898.  898 
  899.  899         $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  900.  900 
  901.  901         $PolicyQualifierInfo = array(
  902.  902             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  903.  903             'children' => array(
  904.  904                 'policyQualifierId' => $PolicyQualifierId,
  905.  905                 'qualifier'         => array('type' => FILE_ASN1_TYPE_ANY)
  906.  906             )
  907.  907         );
  908.  908 
  909.  909         $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  910.  910 
  911.  911         $PolicyInformation = array(
  912.  912             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  913.  913             'children' => array(
  914.  914                 'policyIdentifier' => $CertPolicyId,
  915.  915                 'policyQualifiers' => array(
  916.  916                                           'type'     => FILE_ASN1_TYPE_SEQUENCE,
  917.  917                                           'min'      => 0,
  918.  918                                           'max'      => -1,
  919.  919                                           'optional' => true,
  920.  920                                           'children' => $PolicyQualifierInfo
  921.  921                                       )
  922.  922             )
  923.  923         );
  924.  924 
  925.  925         $this->CertificatePolicies = array(
  926.  926             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  927.  927             'min'      => 1,
  928.  928             'max'      => -1,
  929.  929             'children' => $PolicyInformation
  930.  930         );
  931.  931 
  932.  932         $this->PolicyMappings = array(
  933.  933             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  934.  934             'min'      => 1,
  935.  935             'max'      => -1,
  936.  936             'children' => array(
  937.  937                               'type'     => FILE_ASN1_TYPE_SEQUENCE,
  938.  938                               'children' => array(
  939.  939                                   'issuerDomainPolicy' => $CertPolicyId,
  940.  940                                   'subjectDomainPolicy' => $CertPolicyId
  941.  941                               )
  942.  942                        )
  943.  943         );
  944.  944 
  945.  945         $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  946.  946 
  947.  947         $this->ExtKeyUsageSyntax = array(
  948.  948             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  949.  949             'min'      => 1,
  950.  950             'max'      => -1,
  951.  951             'children' => $KeyPurposeId
  952.  952         );
  953.  953 
  954.  954         $AccessDescription = array(
  955.  955             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  956.  956             'children' => array(
  957.  957                 'accessMethod'   => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
  958.  958                 'accessLocation' => $GeneralName
  959.  959             )
  960.  960         );
  961.  961 
  962.  962         $this->AuthorityInfoAccessSyntax = array(
  963.  963             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  964.  964             'min'      => 1,
  965.  965             'max'      => -1,
  966.  966             'children' => $AccessDescription
  967.  967         );
  968.  968 
  969.  969         $this->SubjectAltName $GeneralNames;
  970.  970 
  971.  971         $this->PrivateKeyUsagePeriod = array(
  972.  972             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  973.  973             'children' => array(
  974.  974                 'notBefore' => array(
  975.  975                                                  'constant' => 0,
  976.  976                                                  'optional' => true,
  977.  977                                                  'implicit' => true,
  978.  978                                                  'type' => FILE_ASN1_TYPE_GENERALIZED_TIME),
  979.  979                 'notAfter'  => array(
  980.  980                                                  'constant' => 1,
  981.  981                                                  'optional' => true,
  982.  982                                                  'implicit' => true,
  983.  983                                                  'type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
  984.  984             )
  985.  985         );
  986.  986 
  987.  987         $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER);
  988.  988 
  989.  989         $GeneralSubtree = array(
  990.  990             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  991.  991             'children' => array(
  992.  992                 'base'    => $GeneralName,
  993.  993                 'minimum' => array(
  994.  994                                  'constant' => 0,
  995.  995                                  'optional' => true,
  996.  996                                  'implicit' => true,
  997.  997                                  'default' => new Math_BigInteger(0)
  998.  998                              ) + $BaseDistance,
  999.  999                 'maximum' => array(
  1000. 1000                                  'constant' => 1,
  1001. 1001                                  'optional' => true,
  1002. 1002                                  'implicit' => true,
  1003. 1003                              ) + $BaseDistance
  1004. 1004             )
  1005. 1005         );
  1006. 1006 
  1007. 1007         $GeneralSubtrees = array(
  1008. 1008             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1009. 1009             'min'      => 1,
  1010. 1010             'max'      => -1,
  1011. 1011             'children' => $GeneralSubtree
  1012. 1012         );
  1013. 1013 
  1014. 1014         $this->NameConstraints = array(
  1015. 1015             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1016. 1016             'children' => array(
  1017. 1017                 'permittedSubtrees' => array(
  1018. 1018                                            'constant' => 0,
  1019. 1019                                            'optional' => true,
  1020. 1020                                            'implicit' => true
  1021. 1021                                        ) + $GeneralSubtrees,
  1022. 1022                 'excludedSubtrees'  => array(
  1023. 1023                                            'constant' => 1,
  1024. 1024                                            'optional' => true,
  1025. 1025                                            'implicit' => true
  1026. 1026                                        ) + $GeneralSubtrees
  1027. 1027             )
  1028. 1028         );
  1029. 1029 
  1030. 1030         $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING);
  1031. 1031 
  1032. 1032         $DisplayText = array(
  1033. 1033             'type'     => FILE_ASN1_TYPE_CHOICE,
  1034. 1034             'children' => array(
  1035. 1035                 'ia5String'     => array('type' => FILE_ASN1_TYPE_IA5_STRING),
  1036. 1036                 'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING),
  1037. 1037                 'bmpString'     => array('type' => FILE_ASN1_TYPE_BMP_STRING),
  1038. 1038                 'utf8String'    => array('type' => FILE_ASN1_TYPE_UTF8_STRING)
  1039. 1039             )
  1040. 1040         );
  1041. 1041 
  1042. 1042         $NoticeReference = array(
  1043. 1043             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1044. 1044             'children' => array(
  1045. 1045                 'organization'  => $DisplayText,
  1046. 1046                 'noticeNumbers' => array(
  1047. 1047                                        'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1048. 1048                                        'min'      => 1,
  1049. 1049                                        'max'      => 200,
  1050. 1050                                        'children' => array('type' => FILE_ASN1_TYPE_INTEGER)
  1051. 1051                                    )
  1052. 1052             )
  1053. 1053         );
  1054. 1054 
  1055. 1055         $this->UserNotice = array(
  1056. 1056             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1057. 1057             'children' => array(
  1058. 1058                 'noticeRef' => array(
  1059. 1059                                            'optional' => true,
  1060. 1060                                            'implicit' => true
  1061. 1061                                        ) + $NoticeReference,
  1062. 1062                 'explicitText'  => array(
  1063. 1063                                            'optional' => true,
  1064. 1064                                            'implicit' => true
  1065. 1065                                        ) + $DisplayText
  1066. 1066             )
  1067. 1067         );
  1068. 1068 
  1069. 1069         // mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
  1070. 1070         $this->netscape_cert_type = array(
  1071. 1071             'type'    => FILE_ASN1_TYPE_BIT_STRING,
  1072. 1072             'mapping' => array(
  1073. 1073                 'SSLClient',
  1074. 1074                 'SSLServer',
  1075. 1075                 'Email',
  1076. 1076                 'ObjectSigning',
  1077. 1077                 'Reserved',
  1078. 1078                 'SSLCA',
  1079. 1079                 'EmailCA',
  1080. 1080                 'ObjectSigningCA'
  1081. 1081             )
  1082. 1082         );
  1083. 1083 
  1084. 1084         $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING);
  1085. 1085         $this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING);
  1086. 1086 
  1087. 1087         // attribute is used in RFC2986 but we're using the RFC5280 definition
  1088. 1088 
  1089. 1089         $Attribute = array(
  1090. 1090             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1091. 1091             'children' => array(
  1092. 1092                 'type' => $AttributeType,
  1093. 1093                 'value'=> array(
  1094. 1094                               'type'     => FILE_ASN1_TYPE_SET,
  1095. 1095                               'min'      => 1,
  1096. 1096                               'max'      => -1,
  1097. 1097                               'children' => $this->AttributeValue
  1098. 1098                           )
  1099. 1099             )
  1100. 1100         );
  1101. 1101 
  1102. 1102         $this->SubjectDirectoryAttributes = array(
  1103. 1103             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1104. 1104             'min'      => 1,
  1105. 1105             'max'      => -1,
  1106. 1106             'children' => $Attribute
  1107. 1107         );
  1108. 1108 
  1109. 1109         // adapted from <http://tools.ietf.org/html/rfc2986>
  1110. 1110 
  1111. 1111         $Attributes = array(
  1112. 1112             'type'     => FILE_ASN1_TYPE_SET,
  1113. 1113             'min'      => 1,
  1114. 1114             'max'      => -1,
  1115. 1115             'children' => $Attribute
  1116. 1116         );
  1117. 1117 
  1118. 1118         $CertificationRequestInfo = array(
  1119. 1119             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1120. 1120             'children' => array(
  1121. 1121                 'version'       => array(
  1122. 1122                                        'type' => FILE_ASN1_TYPE_INTEGER,
  1123. 1123                                        'mapping' => array('v1')
  1124. 1124                                    ),
  1125. 1125                 'subject'       => $this->Name,
  1126. 1126                 'subjectPKInfo' => $SubjectPublicKeyInfo,
  1127. 1127                 'attributes'    => array(
  1128. 1128                                        'constant' => 0,
  1129. 1129                                        'optional' => true,
  1130. 1130                                        'implicit' => true
  1131. 1131                                    ) + $Attributes,
  1132. 1132             )
  1133. 1133         );
  1134. 1134 
  1135. 1135         $this->CertificationRequest = array(
  1136. 1136             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1137. 1137             'children' => array(
  1138. 1138                 'certificationRequestInfo' => $CertificationRequestInfo,
  1139. 1139                 'signatureAlgorithm'       => $AlgorithmIdentifier,
  1140. 1140                 'signature'                => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  1141. 1141             )
  1142. 1142         );
  1143. 1143 
  1144. 1144         $RevokedCertificate = array(
  1145. 1145             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1146. 1146             'children' => array(
  1147. 1147                               'userCertificate'    => $CertificateSerialNumber,
  1148. 1148                               'revocationDate'     => $Time,
  1149. 1149                               'crlEntryExtensions' => array(
  1150. 1150                                                           'optional' => true
  1151. 1151                                                       ) + $this->Extensions
  1152. 1152                           )
  1153. 1153         );
  1154. 1154 
  1155. 1155         $TBSCertList = array(
  1156. 1156             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1157. 1157             'children' => array(
  1158. 1158                 'version'             => array(
  1159. 1159                                              'optional' => true,
  1160. 1160                                              'default'  => 'v1'
  1161. 1161                                          ) + $Version,
  1162. 1162                 'signature'           => $AlgorithmIdentifier,
  1163. 1163                 'issuer'              => $this->Name,
  1164. 1164                 'thisUpdate'          => $Time,
  1165. 1165                 'nextUpdate'          => array(
  1166. 1166                                              'optional' => true
  1167. 1167                                          ) + $Time,
  1168. 1168                 'revokedCertificates' => array(
  1169. 1169                                              'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1170. 1170                                              'optional' => true,
  1171. 1171                                              'min'      => 0,
  1172. 1172                                              'max'      => -1,
  1173. 1173                                              'children' => $RevokedCertificate
  1174. 1174                                          ),
  1175. 1175                 'crlExtensions'       => array(
  1176. 1176                                              'constant' => 0,
  1177. 1177                                              'optional' => true,
  1178. 1178                                              'explicit' => true
  1179. 1179                                          ) + $this->Extensions
  1180. 1180             )
  1181. 1181         );
  1182. 1182 
  1183. 1183         $this->CertificateList = array(
  1184. 1184             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1185. 1185             'children' => array(
  1186. 1186                 'tbsCertList'        => $TBSCertList,
  1187. 1187                 'signatureAlgorithm' => $AlgorithmIdentifier,
  1188. 1188                 'signature'          => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  1189. 1189             )
  1190. 1190         );
  1191. 1191 
  1192. 1192         $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER);
  1193. 1193 
  1194. 1194         $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED,
  1195. 1195            'mapping' => array(
  1196. 1196                             'unspecified',
  1197. 1197                             'keyCompromise',
  1198. 1198                             'cACompromise',
  1199. 1199                             'affiliationChanged',
  1200. 1200                             'superseded',
  1201. 1201                             'cessationOfOperation',
  1202. 1202                             'certificateHold',
  1203. 1203                             // Value 7 is not used.
  1204. 1204                             => 'removeFromCRL',
  1205. 1205                             'privilegeWithdrawn',
  1206. 1206                             'aACompromise'
  1207. 1207             )
  1208. 1208         );
  1209. 1209 
  1210. 1210         $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE,
  1211. 1211             'children' => array(
  1212. 1212                 'distributionPoint'          => array(
  1213. 1213                                                     'constant' => 0,
  1214. 1214                                                     'optional' => true,
  1215. 1215                                                     'explicit' => true
  1216. 1216                                                 ) + $DistributionPointName,
  1217. 1217                 'onlyContainsUserCerts'      => array(
  1218. 1218                                                     'type'     => FILE_ASN1_TYPE_BOOLEAN,
  1219. 1219                                                     'constant' => 1,
  1220. 1220                                                     'optional' => true,
  1221. 1221                                                     'default'  => false,
  1222. 1222                                                     'implicit' => true
  1223. 1223                                                 ),
  1224. 1224                 'onlyContainsCACerts'        => array(
  1225. 1225                                                     'type'     => FILE_ASN1_TYPE_BOOLEAN,
  1226. 1226                                                     'constant' => 2,
  1227. 1227                                                     'optional' => true,
  1228. 1228                                                     'default'  => false,
  1229. 1229                                                     'implicit' => true
  1230. 1230                                                 ),
  1231. 1231                 'onlySomeReasons'           => array(
  1232. 1232                                                     'constant' => 3,
  1233. 1233                                                     'optional' => true,
  1234. 1234                                                     'implicit' => true
  1235. 1235                                                 ) + $ReasonFlags,
  1236. 1236                 'indirectCRL'               => array(
  1237. 1237                                                     'type'     => FILE_ASN1_TYPE_BOOLEAN,
  1238. 1238                                                     'constant' => 4,
  1239. 1239                                                     'optional' => true,
  1240. 1240                                                     'default'  => false,
  1241. 1241                                                     'implicit' => true
  1242. 1242                                                 ),
  1243. 1243                 'onlyContainsAttributeCerts' => array(
  1244. 1244                                                     'type'     => FILE_ASN1_TYPE_BOOLEAN,
  1245. 1245                                                     'constant' => 5,
  1246. 1246                                                     'optional' => true,
  1247. 1247                                                     'default'  => false,
  1248. 1248                                                     'implicit' => true
  1249. 1249                                                 )
  1250. 1250                           )
  1251. 1251         );
  1252. 1252 
  1253. 1253         $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME);
  1254. 1254 
  1255. 1255         $this->CertificateIssuer $GeneralNames;
  1256. 1256 
  1257. 1257         $this->HoldInstructionCode = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);
  1258. 1258 
  1259. 1259         $PublicKeyAndChallenge = array(
  1260. 1260             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1261. 1261             'children' => array(
  1262. 1262                 'spki'      => $SubjectPublicKeyInfo,
  1263. 1263                 'challenge' => array('type' => FILE_ASN1_TYPE_IA5_STRING)
  1264. 1264             )
  1265. 1265         );
  1266. 1266 
  1267. 1267         $this->SignedPublicKeyAndChallenge = array(
  1268. 1268             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1269. 1269             'children' => array(
  1270. 1270                 'publicKeyAndChallenge' => $PublicKeyAndChallenge,
  1271. 1271                 'signatureAlgorithm'    => $AlgorithmIdentifier,
  1272. 1272                 'signature'             => array('type' => FILE_ASN1_TYPE_BIT_STRING)
  1273. 1273             )
  1274. 1274         );
  1275. 1275 
  1276. 1276         $this->PostalAddress = array(
  1277. 1277             'type'     => FILE_ASN1_TYPE_SEQUENCE,
  1278. 1278             'optional' => true,
  1279. 1279             'min'      => 1,
  1280. 1280             'max'      => -1,
  1281. 1281             'children' => $this->DirectoryString
  1282. 1282         );
  1283. 1283 
  1284. 1284         // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
  1285. 1285         $this->oids = array(
  1286. 1286             '1.3.6.1.5.5.7' => 'id-pkix',
  1287. 1287             '1.3.6.1.5.5.7.1' => 'id-pe',
  1288. 1288             '1.3.6.1.5.5.7.2' => 'id-qt',
  1289. 1289             '1.3.6.1.5.5.7.3' => 'id-kp',
  1290. 1290             '1.3.6.1.5.5.7.48' => 'id-ad',
  1291. 1291             '1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
  1292. 1292             '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
  1293. 1293             '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
  1294. 1294             '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
  1295. 1295             '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
  1296. 1296             '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
  1297. 1297             '2.5.4' => 'id-at',
  1298. 1298             '2.5.4.41' => 'id-at-name',
  1299. 1299             '2.5.4.4' => 'id-at-surname',
  1300. 1300             '2.5.4.42' => 'id-at-givenName',
  1301. 1301             '2.5.4.43' => 'id-at-initials',
  1302. 1302             '2.5.4.44' => 'id-at-generationQualifier',
  1303. 1303             '2.5.4.3' => 'id-at-commonName',
  1304. 1304             '2.5.4.7' => 'id-at-localityName',
  1305. 1305             '2.5.4.8' => 'id-at-stateOrProvinceName',
  1306. 1306             '2.5.4.10' => 'id-at-organizationName',
  1307. 1307             '2.5.4.11' => 'id-at-organizationalUnitName',
  1308. 1308             '2.5.4.12' => 'id-at-title',
  1309. 1309             '2.5.4.13' => 'id-at-description',
  1310. 1310             '2.5.4.46' => 'id-at-dnQualifier',
  1311. 1311             '2.5.4.6' => 'id-at-countryName',
  1312. 1312             '2.5.4.5' => 'id-at-serialNumber',
  1313. 1313             '2.5.4.65' => 'id-at-pseudonym',
  1314. 1314             '2.5.4.17' => 'id-at-postalCode',
  1315. 1315             '2.5.4.9' => 'id-at-streetAddress',
  1316. 1316             '2.5.4.45' => 'id-at-uniqueIdentifier',
  1317. 1317             '2.5.4.72' => 'id-at-role',
  1318. 1318             '2.5.4.16' => 'id-at-postalAddress',
  1319. 1319 
  1320. 1320             '0.9.2342.19200300.100.1.25' => 'id-domainComponent',
  1321. 1321             '1.2.840.113549.1.9' => 'pkcs-9',
  1322. 1322             '1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress',
  1323. 1323             '2.5.29' => 'id-ce',
  1324. 1324             '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
  1325. 1325             '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
  1326. 1326             '2.5.29.15' => 'id-ce-keyUsage',
  1327. 1327             '2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
  1328. 1328             '2.5.29.32' => 'id-ce-certificatePolicies',
  1329. 1329             '2.5.29.32.0' => 'anyPolicy',
  1330. 1330 
  1331. 1331             '2.5.29.33' => 'id-ce-policyMappings',
  1332. 1332             '2.5.29.17' => 'id-ce-subjectAltName',
  1333. 1333             '2.5.29.18' => 'id-ce-issuerAltName',
  1334. 1334             '2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
  1335. 1335             '2.5.29.19' => 'id-ce-basicConstraints',
  1336. 1336             '2.5.29.30' => 'id-ce-nameConstraints',
  1337. 1337             '2.5.29.36' => 'id-ce-policyConstraints',
  1338. 1338             '2.5.29.31' => 'id-ce-cRLDistributionPoints',
  1339. 1339             '2.5.29.37' => 'id-ce-extKeyUsage',
  1340. 1340             '2.5.29.37.0' => 'anyExtendedKeyUsage',
  1341. 1341             '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
  1342. 1342             '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
  1343. 1343             '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
  1344. 1344             '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
  1345. 1345             '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
  1346. 1346             '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
  1347. 1347             '2.5.29.54' => 'id-ce-inhibitAnyPolicy',
  1348. 1348             '2.5.29.46' => 'id-ce-freshestCRL',
  1349. 1349             '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
  1350. 1350             '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
  1351. 1351             '2.5.29.20' => 'id-ce-cRLNumber',
  1352. 1352             '2.5.29.28' => 'id-ce-issuingDistributionPoint',
  1353. 1353             '2.5.29.27' => 'id-ce-deltaCRLIndicator',
  1354. 1354             '2.5.29.21' => 'id-ce-cRLReasons',
  1355. 1355             '2.5.29.29' => 'id-ce-certificateIssuer',
  1356. 1356             '2.5.29.23' => 'id-ce-holdInstructionCode',
  1357. 1357             '1.2.840.10040.2' => 'holdInstruction',
  1358. 1358             '1.2.840.10040.2.1' => 'id-holdinstruction-none',
  1359. 1359             '1.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
  1360. 1360             '1.2.840.10040.2.3' => 'id-holdinstruction-reject',
  1361. 1361             '2.5.29.24' => 'id-ce-invalidityDate',
  1362. 1362 
  1363. 1363             '1.2.840.113549.2.2' => 'md2',
  1364. 1364             '1.2.840.113549.2.5' => 'md5',
  1365. 1365             '1.3.14.3.2.26' => 'id-sha1',
  1366. 1366             '1.2.840.10040.4.1' => 'id-dsa',
  1367. 1367             '1.2.840.10040.4.3' => 'id-dsa-with-sha1',
  1368. 1368             '1.2.840.113549.1.1' => 'pkcs-1',
  1369. 1369             '1.2.840.113549.1.1.1' => 'rsaEncryption',
  1370. 1370             '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
  1371. 1371             '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
  1372. 1372             '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
  1373. 1373             '1.2.840.10046.2.1' => 'dhpublicnumber',
  1374. 1374             '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
  1375. 1375             '1.2.840.10045' => 'ansi-X9-62',
  1376. 1376             '1.2.840.10045.4' => 'id-ecSigType',
  1377. 1377             '1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
  1378. 1378             '1.2.840.10045.1' => 'id-fieldType',
  1379. 1379             '1.2.840.10045.1.1' => 'prime-field',
  1380. 1380             '1.2.840.10045.1.2' => 'characteristic-two-field',
  1381. 1381             '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
  1382. 1382             '1.2.840.10045.1.2.3.1' => 'gnBasis',
  1383. 1383             '1.2.840.10045.1.2.3.2' => 'tpBasis',
  1384. 1384             '1.2.840.10045.1.2.3.3' => 'ppBasis',
  1385. 1385             '1.2.840.10045.2' => 'id-publicKeyType',
  1386. 1386             '1.2.840.10045.2.1' => 'id-ecPublicKey',
  1387. 1387             '1.2.840.10045.3' => 'ellipticCurve',
  1388. 1388             '1.2.840.10045.3.0' => 'c-TwoCurve',
  1389. 1389             '1.2.840.10045.3.0.1' => 'c2pnb163v1',
  1390. 1390             '1.2.840.10045.3.0.2' => 'c2pnb163v2',
  1391. 1391             '1.2.840.10045.3.0.3' => 'c2pnb163v3',
  1392. 1392             '1.2.840.10045.3.0.4' => 'c2pnb176w1',
  1393. 1393             '1.2.840.10045.3.0.5' => 'c2pnb191v1',
  1394. 1394             '1.2.840.10045.3.0.6' => 'c2pnb191v2',
  1395. 1395             '1.2.840.10045.3.0.7' => 'c2pnb191v3',
  1396. 1396             '1.2.840.10045.3.0.8' => 'c2pnb191v4',
  1397. 1397             '1.2.840.10045.3.0.9' => 'c2pnb191v5',
  1398. 1398             '1.2.840.10045.3.0.10' => 'c2pnb208w1',
  1399. 1399             '1.2.840.10045.3.0.11' => 'c2pnb239v1',
  1400. 1400             '1.2.840.10045.3.0.12' => 'c2pnb239v2',
  1401. 1401             '1.2.840.10045.3.0.13' => 'c2pnb239v3',
  1402. 1402             '1.2.840.10045.3.0.14' => 'c2pnb239v4',
  1403. 1403             '1.2.840.10045.3.0.15' => 'c2pnb239v5',
  1404. 1404             '1.2.840.10045.3.0.16' => 'c2pnb272w1',
  1405. 1405             '1.2.840.10045.3.0.17' => 'c2pnb304w1',
  1406. 1406             '1.2.840.10045.3.0.18' => 'c2pnb359v1',
  1407. 1407             '1.2.840.10045.3.0.19' => 'c2pnb368w1',
  1408. 1408             '1.2.840.10045.3.0.20' => 'c2pnb431r1',
  1409. 1409             '1.2.840.10045.3.1' => 'primeCurve',
  1410. 1410             '1.2.840.10045.3.1.1' => 'prime192v1',
  1411. 1411             '1.2.840.10045.3.1.2' => 'prime192v2',
  1412. 1412             '1.2.840.10045.3.1.3' => 'prime192v3',
  1413. 1413             '1.2.840.10045.3.1.4' => 'prime239v1',
  1414. 1414             '1.2.840.10045.3.1.5' => 'prime239v2',
  1415. 1415             '1.2.840.10045.3.1.6' => 'prime239v3',
  1416. 1416             '1.2.840.10045.3.1.7' => 'prime256v1',
  1417. 1417             '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
  1418. 1418             '1.2.840.113549.1.1.9' => 'id-pSpecified',
  1419. 1419             '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
  1420. 1420             '1.2.840.113549.1.1.8' => 'id-mgf1',
  1421. 1421             '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
  1422. 1422             '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
  1423. 1423             '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
  1424. 1424             '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
  1425. 1425             '2.16.840.1.101.3.4.2.4' => 'id-sha224',
  1426. 1426             '2.16.840.1.101.3.4.2.1' => 'id-sha256',
  1427. 1427             '2.16.840.1.101.3.4.2.2' => 'id-sha384',
  1428. 1428             '2.16.840.1.101.3.4.2.3' => 'id-sha512',
  1429. 1429             '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
  1430. 1430             '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
  1431. 1431             '1.2.643.2.2.20' => 'id-GostR3410-2001',
  1432. 1432             '1.2.643.2.2.19' => 'id-GostR3410-94',
  1433. 1433             // Netscape Object Identifiers from "Netscape Certificate Extensions"
  1434. 1434             '2.16.840.1.113730' => 'netscape',
  1435. 1435             '2.16.840.1.113730.1' => 'netscape-cert-extension',
  1436. 1436             '2.16.840.1.113730.1.1' => 'netscape-cert-type',
  1437. 1437             '2.16.840.1.113730.1.13' => 'netscape-comment',
  1438. 1438             '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
  1439. 1439             // the following are X.509 extensions not supported by phpseclib
  1440. 1440             '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
  1441. 1441             '1.2.840.113533.7.65.0' => 'entrustVersInfo',
  1442. 1442             '2.16.840.1.113733.1.6.9' => 'verisignPrivate',
  1443. 1443             // for Certificate Signing Requests
  1444. 1444             // see http://tools.ietf.org/html/rfc2985
  1445. 1445             '1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName'// PKCS #9 unstructured name
  1446. 1446             '1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword'// Challenge password for certificate revocations
  1447. 1447             '1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' // Certificate extension request
  1448. 1448         );
  1449. 1449     }
  1450. 1450 
  1451. 1451     /**
  1452. 1452      * PHP4 compatible Default Constructor.
  1453. 1453      *
  1454. 1454      * @see self::__construct()
  1455. 1455      * @access public
  1456. 1456      */
  1457. 1457     function File_X509()
  1458. 1458     {
  1459. 1459         $this->__construct();
  1460. 1460     }
  1461. 1461 
  1462. 1462     /**
  1463. 1463      * Load X.509 certificate
  1464. 1464      *
  1465. 1465      * Returns an associative array describing the X.509 cert or a false if the cert failed to load
  1466. 1466      *
  1467. 1467      * @param string $cert
  1468. 1468      * @param int $mode
  1469. 1469      * @access public
  1470. 1470      * @return mixed
  1471. 1471      */
  1472. 1472     function loadX509($cert$mode FILE_X509_FORMAT_AUTO_DETECT)
  1473. 1473     {
  1474. 1474         if (is_array($cert) && isset($cert['tbsCertificate'])) {
  1475. 1475             unset($this->currentCert);
  1476. 1476             unset($this->currentKeyIdentifier);
  1477. 1477             $this->dn $cert['tbsCertificate']['subject'];
  1478. 1478             if (!isset($this->dn)) {
  1479. 1479                 return false;
  1480. 1480             }
  1481. 1481             $this->currentCert $cert;
  1482. 1482 
  1483. 1483             $currentKeyIdentifier $this->getExtension('id-ce-subjectKeyIdentifier');
  1484. 1484             $this->currentKeyIdentifier is_string($currentKeyIdentifier) ? $currentKeyIdentifier null;
  1485. 1485 
  1486. 1486             unset($this->signatureSubject);
  1487. 1487 
  1488. 1488             return $cert;
  1489. 1489         }
  1490. 1490 
  1491. 1491         $asn1 = new File_ASN1();
  1492. 1492 
  1493. 1493         if ($mode != FILE_X509_FORMAT_DER) {
  1494. 1494             $newcert $this->_extractBER($cert);
  1495. 1495             if ($mode == FILE_X509_FORMAT_PEM && $cert == $newcert) {
  1496. 1496                 return false;
  1497. 1497             }
  1498. 1498             $cert $newcert;
  1499. 1499         }
  1500. 1500 
  1501. 1501         if ($cert === false) {
  1502. 1502             $this->currentCert false;
  1503. 1503             return false;
  1504. 1504         }
  1505. 1505 
  1506. 1506         $asn1->loadOIDs($this->oids);
  1507. 1507         $decoded $asn1->decodeBER($cert);
  1508. 1508 
  1509. 1509         if (!empty($decoded)) {
  1510. 1510             $x509 $asn1->asn1map($decoded[0], $this->Certificate);
  1511. 1511         }
  1512. 1512         if (!isset($x509) || $x509 === false) {
  1513. 1513             $this->currentCert false;
  1514. 1514             return false;
  1515. 1515         }
  1516. 1516 
  1517. 1517         $this->signatureSubject substr($cert$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
  1518. 1518 
  1519. 1519         if ($this->_isSubArrayValid($x509'tbsCertificate/extensions')) {
  1520. 1520             $this->_mapInExtensions($x509'tbsCertificate/extensions'$asn1);
  1521. 1521         }
  1522. 1522         $this->_mapInDNs($x509'tbsCertificate/issuer/rdnSequence'$asn1);
  1523. 1523         $this->_mapInDNs($x509'tbsCertificate/subject/rdnSequence'$asn1);
  1524. 1524 
  1525. 1525         $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
  1526. 1526         $key $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
  1527. 1527 
  1528. 1528         $this->currentCert $x509;
  1529. 1529         $this->dn $x509['tbsCertificate']['subject'];
  1530. 1530 
  1531. 1531         $currentKeyIdentifier $this->getExtension('id-ce-subjectKeyIdentifier');
  1532. 1532         $this->currentKeyIdentifier is_string($currentKeyIdentifier) ? $currentKeyIdentifier null;
  1533. 1533 
  1534. 1534         return $x509;
  1535. 1535     }
  1536. 1536 
  1537. 1537     /**
  1538. 1538      * Save X.509 certificate
  1539. 1539      *
  1540. 1540      * @param array $cert
  1541. 1541      * @param int $format optional
  1542. 1542      * @access public
  1543. 1543      * @return string
  1544. 1544      */
  1545. 1545     function saveX509($cert$format FILE_X509_FORMAT_PEM)
  1546. 1546     {
  1547. 1547         if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
  1548. 1548             return false;
  1549. 1549         }
  1550. 1550 
  1551. 1551         switch (true) {
  1552. 1552             // "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()"
  1553. 1553             case !($algorithm $this->_subArray($cert'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
  1554. 1554             case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
  1555. 1555                 break;
  1556. 1556             default:
  1557. 1557                 switch ($algorithm) {
  1558. 1558                     case 'rsaEncryption':
  1559. 1559                         $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
  1560. 1560                             = base64_encode("\0" base64_decode(preg_replace('#-.+-|[\r\n]#'''$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
  1561. 1561                         /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier."
  1562. 1562                            -- https://tools.ietf.org/html/rfc3279#section-2.3.1
  1563. 1563 
  1564. 1564                            given that and the fact that RSA keys appear ot be the only key type for which the parameters field can be blank,
  1565. 1565                            it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever.
  1566. 1566                          */
  1567. 1567                         $cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null;
  1568. 1568                         // https://tools.ietf.org/html/rfc3279#section-2.2.1
  1569. 1569                         $cert['signatureAlgorithm']['parameters'] = null;
  1570. 1570                         $cert['tbsCertificate']['signature']['parameters'] = null;
  1571. 1571                 }
  1572. 1572         }
  1573. 1573 
  1574. 1574         $asn1 = new File_ASN1();
  1575. 1575         $asn1->loadOIDs($this->oids);
  1576. 1576 
  1577. 1577         $filters = array();
  1578. 1578         $type_utf8_string = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  1579. 1579         $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string;
  1580. 1580         $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string;
  1581. 1581         $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string;
  1582. 1582         $filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string;
  1583. 1583         $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string;
  1584. 1584         $filters['signatureAlgorithm']['parameters'] = $type_utf8_string;
  1585. 1585         $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
  1586. 1586         //$filters['policyQualifiers']['qualifier'] = $type_utf8_string;
  1587. 1587         $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
  1588. 1588         $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string;
  1589. 1589 
  1590. 1590         /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING.
  1591. 1591            FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
  1592. 1592            characters.
  1593. 1593          */
  1594. 1594         $filters['policyQualifiers']['qualifier']
  1595. 1595             = array('type' => FILE_ASN1_TYPE_IA5_STRING);
  1596. 1596 
  1597. 1597         $asn1->loadFilters($filters);
  1598. 1598 
  1599. 1599         $this->_mapOutExtensions($cert'tbsCertificate/extensions'$asn1);
  1600. 1600         $this->_mapOutDNs($cert'tbsCertificate/issuer/rdnSequence'$asn1);
  1601. 1601         $this->_mapOutDNs($cert'tbsCertificate/subject/rdnSequence'$asn1);
  1602. 1602 
  1603. 1603         $cert $asn1->encodeDER($cert$this->Certificate);
  1604. 1604 
  1605. 1605         switch ($format) {
  1606. 1606             case FILE_X509_FORMAT_DER:
  1607. 1607                 return $cert;
  1608. 1608             // case FILE_X509_FORMAT_PEM:
  1609. 1609             default:
  1610. 1610                 return "-----BEGIN CERTIFICATE-----\r\n" chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----';
  1611. 1611         }
  1612. 1612     }
  1613. 1613 
  1614. 1614     /**
  1615. 1615      * Map extension values from octet string to extension-specific internal
  1616. 1616      *   format.
  1617. 1617      *
  1618. 1618      * @param array ref $root
  1619. 1619      * @param string $path
  1620. 1620      * @param object $asn1
  1621. 1621      * @access private
  1622. 1622      */
  1623. 1623     function _mapInExtensions(&$root$path$asn1)
  1624. 1624     {
  1625. 1625         $extensions = &$this->_subArrayUnchecked($root$path);
  1626. 1626 
  1627. 1627         if ($extensions) {
  1628. 1628             for ($i 0$i count($extensions); $i++) {
  1629. 1629                 $id $extensions[$i]['extnId'];
  1630. 1630                 $value = &$extensions[$i]['extnValue'];
  1631. 1631                 $value base64_decode($value);
  1632. 1632                 $decoded $asn1->decodeBER($value);
  1633. 1633                 /* [extnValue] contains the DER encoding of an ASN.1 value
  1634. 1634                    corresponding to the extension type identified by extnID */
  1635. 1635                 $map $this->_getMapping($id);
  1636. 1636                 if (!is_bool($map)) {
  1637. 1637                     $mapped $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this'_decodeIP')));
  1638. 1638                     $value $mapped === false $decoded[0] : $mapped;
  1639. 1639 
  1640. 1640                     if ($id == 'id-ce-certificatePolicies') {
  1641. 1641                         for ($j 0$j count($value); $j++) {
  1642. 1642                             if (!isset($value[$j]['policyQualifiers'])) {
  1643. 1643                                 continue;
  1644. 1644                             }
  1645. 1645                             for ($k 0$k count($value[$j]['policyQualifiers']); $k++) {
  1646. 1646                                 $subid $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
  1647. 1647                                 $map $this->_getMapping($subid);
  1648. 1648                                 $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
  1649. 1649                                 if ($map !== false) {
  1650. 1650                                     $decoded $asn1->decodeBER($subvalue);
  1651. 1651                                     $mapped $asn1->asn1map($decoded[0], $map);
  1652. 1652                                     $subvalue $mapped === false $decoded[0] : $mapped;
  1653. 1653                                 }
  1654. 1654                             }
  1655. 1655                         }
  1656. 1656                     }
  1657. 1657                 } else {
  1658. 1658                     $value base64_encode($value);
  1659. 1659                 }
  1660. 1660             }
  1661. 1661         }
  1662. 1662     }
  1663. 1663 
  1664. 1664     /**
  1665. 1665      * Map extension values from extension-specific internal format to
  1666. 1666      *   octet string.
  1667. 1667      *
  1668. 1668      * @param array ref $root
  1669. 1669      * @param string $path
  1670. 1670      * @param object $asn1
  1671. 1671      * @access private
  1672. 1672      */
  1673. 1673     function _mapOutExtensions(&$root$path$asn1)
  1674. 1674     {
  1675. 1675         $extensions = &$this->_subArray($root$path);
  1676. 1676 
  1677. 1677         if (is_array($extensions)) {
  1678. 1678             $size count($extensions);
  1679. 1679             for ($i 0$i $size$i++) {
  1680. 1680                 if (is_object($extensions[$i]) && strtolower(get_class($extensions[$i])) == 'file_asn1_element') {
  1681. 1681                     continue;
  1682. 1682                 }
  1683. 1683 
  1684. 1684                 $id $extensions[$i]['extnId'];
  1685. 1685                 $value = &$extensions[$i]['extnValue'];
  1686. 1686 
  1687. 1687                 switch ($id) {
  1688. 1688                     case 'id-ce-certificatePolicies':
  1689. 1689                         for ($j 0$j count($value); $j++) {
  1690. 1690                             if (!isset($value[$j]['policyQualifiers'])) {
  1691. 1691                                 continue;
  1692. 1692                             }
  1693. 1693                             for ($k 0$k count($value[$j]['policyQualifiers']); $k++) {
  1694. 1694                                 $subid $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
  1695. 1695                                 $map $this->_getMapping($subid);
  1696. 1696                                 $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
  1697. 1697                                 if ($map !== false) {
  1698. 1698                                     // by default File_ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's
  1699. 1699                                     // actual type is FILE_ASN1_TYPE_ANY
  1700. 1700                                     $subvalue = new File_ASN1_Element($asn1->encodeDER($subvalue$map));
  1701. 1701                                 }
  1702. 1702                             }
  1703. 1703                         }
  1704. 1704                         break;
  1705. 1705                     case 'id-ce-authorityKeyIdentifier'// use 00 as the serial number instead of an empty string
  1706. 1706                         if (isset($value['authorityCertSerialNumber'])) {
  1707. 1707                             if ($value['authorityCertSerialNumber']->toBytes() == '') {
  1708. 1708                                 $temp chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0";
  1709. 1709                                 $value['authorityCertSerialNumber'] = new File_ASN1_Element($temp);
  1710. 1710                             }
  1711. 1711                         }
  1712. 1712                 }
  1713. 1713 
  1714. 1714                 /* [extnValue] contains the DER encoding of an ASN.1 value
  1715. 1715                    corresponding to the extension type identified by extnID */
  1716. 1716                 $map $this->_getMapping($id);
  1717. 1717                 if (is_bool($map)) {
  1718. 1718                     if (!$map) {
  1719. 1719                         user_error($id ' is not a currently supported extension');
  1720. 1720                         unset($extensions[$i]);
  1721. 1721                     }
  1722. 1722                 } else {
  1723. 1723                     $temp $asn1->encodeDER($value$map, array('iPAddress' => array($this'_encodeIP')));
  1724. 1724                     $value base64_encode($temp);
  1725. 1725                 }
  1726. 1726             }
  1727. 1727         }
  1728. 1728     }
  1729. 1729 
  1730. 1730     /**
  1731. 1731      * Map attribute values from ANY type to attribute-specific internal
  1732. 1732      *   format.
  1733. 1733      *
  1734. 1734      * @param array ref $root
  1735. 1735      * @param string $path
  1736. 1736      * @param object $asn1
  1737. 1737      * @access private
  1738. 1738      */
  1739. 1739     function _mapInAttributes(&$root$path$asn1)
  1740. 1740     {
  1741. 1741         $attributes = &$this->_subArray($root$path);
  1742. 1742 
  1743. 1743         if (is_array($attributes)) {
  1744. 1744             for ($i 0$i count($attributes); $i++) {
  1745. 1745                 $id $attributes[$i]['type'];
  1746. 1746                 /* $value contains the DER encoding of an ASN.1 value
  1747. 1747                    corresponding to the attribute type identified by type */
  1748. 1748                 $map $this->_getMapping($id);
  1749. 1749                 if (is_array($attributes[$i]['value'])) {
  1750. 1750                     $values = &$attributes[$i]['value'];
  1751. 1751                     for ($j 0$j count($values); $j++) {
  1752. 1752                         $value $asn1->encodeDER($values[$j], $this->AttributeValue);
  1753. 1753                         $decoded $asn1->decodeBER($value);
  1754. 1754                         if (!is_bool($map)) {
  1755. 1755                             $mapped $asn1->asn1map($decoded[0], $map);
  1756. 1756                             if ($mapped !== false) {
  1757. 1757                                 $values[$j] = $mapped;
  1758. 1758                             }
  1759. 1759                             if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values$j)) {
  1760. 1760                                 $this->_mapInExtensions($values$j$asn1);
  1761. 1761                             }
  1762. 1762                         } elseif ($map) {
  1763. 1763                             $values[$j] = base64_encode($value);
  1764. 1764                         }
  1765. 1765                     }
  1766. 1766                 }
  1767. 1767             }
  1768. 1768         }
  1769. 1769     }
  1770. 1770 
  1771. 1771     /**
  1772. 1772      * Map attribute values from attribute-specific internal format to
  1773. 1773      *   ANY type.
  1774. 1774      *
  1775. 1775      * @param array ref $root
  1776. 1776      * @param string $path
  1777. 1777      * @param object $asn1
  1778. 1778      * @access private
  1779. 1779      */
  1780. 1780     function _mapOutAttributes(&$root$path$asn1)
  1781. 1781     {
  1782. 1782         $attributes = &$this->_subArray($root$path);
  1783. 1783 
  1784. 1784         if (is_array($attributes)) {
  1785. 1785             $size count($attributes);
  1786. 1786             for ($i 0$i $size$i++) {
  1787. 1787                 /* [value] contains the DER encoding of an ASN.1 value
  1788. 1788                    corresponding to the attribute type identified by type */
  1789. 1789                 $id $attributes[$i]['type'];
  1790. 1790                 $map $this->_getMapping($id);
  1791. 1791                 if ($map === false) {
  1792. 1792                     user_error($id ' is not a currently supported attribute'E_USER_NOTICE);
  1793. 1793                     unset($attributes[$i]);
  1794. 1794                 } elseif (is_array($attributes[$i]['value'])) {
  1795. 1795                     $values = &$attributes[$i]['value'];
  1796. 1796                     for ($j 0$j count($values); $j++) {
  1797. 1797                         switch ($id) {
  1798. 1798                             case 'pkcs-9-at-extensionRequest':
  1799. 1799                                 $this->_mapOutExtensions($values$j$asn1);
  1800. 1800                                 break;
  1801. 1801                         }
  1802. 1802 
  1803. 1803                         if (!is_bool($map)) {
  1804. 1804                             $temp $asn1->encodeDER($values[$j], $map);
  1805. 1805                             $decoded $asn1->decodeBER($temp);
  1806. 1806                             $values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue);
  1807. 1807                         }
  1808. 1808                     }
  1809. 1809                 }
  1810. 1810             }
  1811. 1811         }
  1812. 1812     }
  1813. 1813 
  1814. 1814     /**
  1815. 1815      * Map DN values from ANY type to DN-specific internal
  1816. 1816      *   format.
  1817. 1817      *
  1818. 1818      * @param array ref $root
  1819. 1819      * @param string $path
  1820. 1820      * @param object $asn1
  1821. 1821      * @access private
  1822. 1822      */
  1823. 1823     function _mapInDNs(&$root$path$asn1)
  1824. 1824     {
  1825. 1825         $dns = &$this->_subArray($root$path);
  1826. 1826 
  1827. 1827         if (is_array($dns)) {
  1828. 1828             for ($i 0$i count($dns); $i++) {
  1829. 1829                 for ($j 0$j count($dns[$i]); $j++) {
  1830. 1830                     $type $dns[$i][$j]['type'];
  1831. 1831                     $value = &$dns[$i][$j]['value'];
  1832. 1832                     if (is_object($value) && strtolower(get_class($value)) == 'file_asn1_element') {
  1833. 1833                         $map $this->_getMapping($type);
  1834. 1834                         if (!is_bool($map)) {
  1835. 1835                             $decoded $asn1->decodeBER($value);
  1836. 1836                             $value $asn1->asn1map($decoded[0], $map);
  1837. 1837                         }
  1838. 1838                     }
  1839. 1839                 }
  1840. 1840             }
  1841. 1841         }
  1842. 1842     }
  1843. 1843 
  1844. 1844     /**
  1845. 1845      * Map DN values from DN-specific internal format to
  1846. 1846      *   ANY type.
  1847. 1847      *
  1848. 1848      * @param array ref $root
  1849. 1849      * @param string $path
  1850. 1850      * @param object $asn1
  1851. 1851      * @access private
  1852. 1852      */
  1853. 1853     function _mapOutDNs(&$root$path$asn1)
  1854. 1854     {
  1855. 1855         $dns = &$this->_subArray($root$path);
  1856. 1856 
  1857. 1857         if (is_array($dns)) {
  1858. 1858             $size count($dns);
  1859. 1859             for ($i 0$i $size$i++) {
  1860. 1860                 for ($j 0$j count($dns[$i]); $j++) {
  1861. 1861                     $type $dns[$i][$j]['type'];
  1862. 1862                     $value = &$dns[$i][$j]['value'];
  1863. 1863                     if (is_object($value) && strtolower(get_class($value)) == 'file_asn1_element') {
  1864. 1864                         continue;
  1865. 1865                     }
  1866. 1866 
  1867. 1867                     $map $this->_getMapping($type);
  1868. 1868                     if (!is_bool($map)) {
  1869. 1869                         $value = new File_ASN1_Element($asn1->encodeDER($value$map));
  1870. 1870                     }
  1871. 1871                 }
  1872. 1872             }
  1873. 1873         }
  1874. 1874     }
  1875. 1875 
  1876. 1876     /**
  1877. 1877      * Associate an extension ID to an extension mapping
  1878. 1878      *
  1879. 1879      * @param string $extnId
  1880. 1880      * @access private
  1881. 1881      * @return mixed
  1882. 1882      */
  1883. 1883     function _getMapping($extnId)
  1884. 1884     {
  1885. 1885         if (!is_string($extnId)) { // eg. if it's a File_ASN1_Element object
  1886. 1886             return true;
  1887. 1887         }
  1888. 1888 
  1889. 1889         switch ($extnId) {
  1890. 1890             case 'id-ce-keyUsage':
  1891. 1891                 return $this->KeyUsage;
  1892. 1892             case 'id-ce-basicConstraints':
  1893. 1893                 return $this->BasicConstraints;
  1894. 1894             case 'id-ce-subjectKeyIdentifier':
  1895. 1895                 return $this->KeyIdentifier;
  1896. 1896             case 'id-ce-cRLDistributionPoints':
  1897. 1897                 return $this->CRLDistributionPoints;
  1898. 1898             case 'id-ce-authorityKeyIdentifier':
  1899. 1899                 return $this->AuthorityKeyIdentifier;
  1900. 1900             case 'id-ce-certificatePolicies':
  1901. 1901                 return $this->CertificatePolicies;
  1902. 1902             case 'id-ce-extKeyUsage':
  1903. 1903                 return $this->ExtKeyUsageSyntax;
  1904. 1904             case 'id-pe-authorityInfoAccess':
  1905. 1905                 return $this->AuthorityInfoAccessSyntax;
  1906. 1906             case 'id-ce-subjectAltName':
  1907. 1907                 return $this->SubjectAltName;
  1908. 1908             case 'id-ce-subjectDirectoryAttributes':
  1909. 1909                 return $this->SubjectDirectoryAttributes;
  1910. 1910             case 'id-ce-privateKeyUsagePeriod':
  1911. 1911                 return $this->PrivateKeyUsagePeriod;
  1912. 1912             case 'id-ce-issuerAltName':
  1913. 1913                 return $this->IssuerAltName;
  1914. 1914             case 'id-ce-policyMappings':
  1915. 1915                 return $this->PolicyMappings;
  1916. 1916             case 'id-ce-nameConstraints':
  1917. 1917                 return $this->NameConstraints;
  1918. 1918 
  1919. 1919             case 'netscape-cert-type':
  1920. 1920                 return $this->netscape_cert_type;
  1921. 1921             case 'netscape-comment':
  1922. 1922                 return $this->netscape_comment;
  1923. 1923             case 'netscape-ca-policy-url':
  1924. 1924                 return $this->netscape_ca_policy_url;
  1925. 1925 
  1926. 1926             // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets
  1927. 1927             // back around to asn1map() and we don't want it decoded again.
  1928. 1928             //case 'id-qt-cps':
  1929. 1929             //    return $this->CPSuri;
  1930. 1930             case 'id-qt-unotice':
  1931. 1931                 return $this->UserNotice;
  1932. 1932 
  1933. 1933             // the following OIDs are unsupported but we don't want them to give notices when calling saveX509().
  1934. 1934             case 'id-pe-logotype'// http://www.ietf.org/rfc/rfc3709.txt
  1935. 1935             case 'entrustVersInfo':
  1936. 1936             // http://support.microsoft.com/kb/287547
  1937. 1937             case '1.3.6.1.4.1.311.20.2'// szOID_ENROLL_CERTTYPE_EXTENSION
  1938. 1938             case '1.3.6.1.4.1.311.21.1'// szOID_CERTSRV_CA_VERSION
  1939. 1939             // "SET Secure Electronic Transaction Specification"
  1940. 1940             // http://www.maithean.com/docs/set_bk3.pdf
  1941. 1941             case '2.23.42.7.0'// id-set-hashedRootKey
  1942. 1942             // "Certificate Transparency"
  1943. 1943             // https://tools.ietf.org/html/rfc6962
  1944. 1944             case '1.3.6.1.4.1.11129.2.4.2':
  1945. 1945                 return true;
  1946. 1946 
  1947. 1947             // CSR attributes
  1948. 1948             case 'pkcs-9-at-unstructuredName':
  1949. 1949                 return $this->PKCS9String;
  1950. 1950             case 'pkcs-9-at-challengePassword':
  1951. 1951                 return $this->DirectoryString;
  1952. 1952             case 'pkcs-9-at-extensionRequest':
  1953. 1953                 return $this->Extensions;
  1954. 1954 
  1955. 1955             // CRL extensions.
  1956. 1956             case 'id-ce-cRLNumber':
  1957. 1957                 return $this->CRLNumber;
  1958. 1958             case 'id-ce-deltaCRLIndicator':
  1959. 1959                 return $this->CRLNumber;
  1960. 1960             case 'id-ce-issuingDistributionPoint':
  1961. 1961                 return $this->IssuingDistributionPoint;
  1962. 1962             case 'id-ce-freshestCRL':
  1963. 1963                 return $this->CRLDistributionPoints;
  1964. 1964             case 'id-ce-cRLReasons':
  1965. 1965                 return $this->CRLReason;
  1966. 1966             case 'id-ce-invalidityDate':
  1967. 1967                 return $this->InvalidityDate;
  1968. 1968             case 'id-ce-certificateIssuer':
  1969. 1969                 return $this->CertificateIssuer;
  1970. 1970             case 'id-ce-holdInstructionCode':
  1971. 1971                 return $this->HoldInstructionCode;
  1972. 1972             case 'id-at-postalAddress':
  1973. 1973                 return $this->PostalAddress;
  1974. 1974         }
  1975. 1975 
  1976. 1976         return false;
  1977. 1977     }
  1978. 1978 
  1979. 1979     /**
  1980. 1980      * Load an X.509 certificate as a certificate authority
  1981. 1981      *
  1982. 1982      * @param string $cert
  1983. 1983      * @access public
  1984. 1984      * @return bool
  1985. 1985      */
  1986. 1986     function loadCA($cert)
  1987. 1987     {
  1988. 1988         $olddn $this->dn;
  1989. 1989         $oldcert $this->currentCert;
  1990. 1990         $oldsigsubj $this->signatureSubject;
  1991. 1991         $oldkeyid $this->currentKeyIdentifier;
  1992. 1992 
  1993. 1993         $cert $this->loadX509($cert);
  1994. 1994         if (!$cert) {
  1995. 1995             $this->dn $olddn;
  1996. 1996             $this->currentCert $oldcert;
  1997. 1997             $this->signatureSubject $oldsigsubj;
  1998. 1998             $this->currentKeyIdentifier $oldkeyid;
  1999. 1999 
  2000. 2000             return false;
  2001. 2001         }
  2002. 2002 
  2003. 2003         /* From RFC5280 "PKIX Certificate and CRL Profile":
  2004. 2004 
  2005. 2005            If the keyUsage extension is present, then the subject public key
  2006. 2006            MUST NOT be used to verify signatures on certificates or CRLs unless
  2007. 2007            the corresponding keyCertSign or cRLSign bit is set. */
  2008. 2008         //$keyUsage = $this->getExtension('id-ce-keyUsage');
  2009. 2009         //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) {
  2010. 2010         //    return false;
  2011. 2011         //}
  2012. 2012 
  2013. 2013         /* From RFC5280 "PKIX Certificate and CRL Profile":
  2014. 2014 
  2015. 2015            The cA boolean indicates whether the certified public key may be used
  2016. 2016            to verify certificate signatures.  If the cA boolean is not asserted,
  2017. 2017            then the keyCertSign bit in the key usage extension MUST NOT be
  2018. 2018            asserted.  If the basic constraints extension is not present in a
  2019. 2019            version 3 certificate, or the extension is present but the cA boolean
  2020. 2020            is not asserted, then the certified public key MUST NOT be used to
  2021. 2021            verify certificate signatures. */
  2022. 2022         //$basicConstraints = $this->getExtension('id-ce-basicConstraints');
  2023. 2023         //if (!$basicConstraints || !$basicConstraints['cA']) {
  2024. 2024         //    return false;
  2025. 2025         //}
  2026. 2026 
  2027. 2027         $this->CAs[] = $cert;
  2028. 2028 
  2029. 2029         $this->dn $olddn;
  2030. 2030         $this->currentCert $oldcert;
  2031. 2031         $this->signatureSubject $oldsigsubj;
  2032. 2032 
  2033. 2033         return true;
  2034. 2034     }
  2035. 2035 
  2036. 2036     /**
  2037. 2037      * Validate an X.509 certificate against a URL
  2038. 2038      *
  2039. 2039      * From RFC2818 "HTTP over TLS":
  2040. 2040      *
  2041. 2041      * Matching is performed using the matching rules specified by
  2042. 2042      * [RFC2459].  If more than one identity of a given type is present in
  2043. 2043      * the certificate (e.g., more than one dNSName name, a match in any one
  2044. 2044      * of the set is considered acceptable.) Names may contain the wildcard
  2045. 2045      * character * which is considered to match any single domain name
  2046. 2046      * component or component fragment. E.g., *.a.com matches foo.a.com but
  2047. 2047      * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
  2048. 2048      *
  2049. 2049      * @param string $url
  2050. 2050      * @access public
  2051. 2051      * @return bool
  2052. 2052      */
  2053. 2053     function validateURL($url)
  2054. 2054     {
  2055. 2055         if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
  2056. 2056             return false;
  2057. 2057         }
  2058. 2058 
  2059. 2059         $components parse_url($url);
  2060. 2060         if (!isset($components['host'])) {
  2061. 2061             return false;
  2062. 2062         }
  2063. 2063 
  2064. 2064         if ($names $this->getExtension('id-ce-subjectAltName')) {
  2065. 2065             foreach ($names as $name) {
  2066. 2066                 foreach ($name as $key => $value) {
  2067. 2067                     $value str_replace(array('.''*'), array('\.''[^.]*'), $value);
  2068. 2068                     switch ($key) {
  2069. 2069                         case 'dNSName':
  2070. 2070                             /* From RFC2818 "HTTP over TLS":
  2071. 2071 
  2072. 2072                                If a subjectAltName extension of type dNSName is present, that MUST
  2073. 2073                                be used as the identity. Otherwise, the (most specific) Common Name
  2074. 2074                                field in the Subject field of the certificate MUST be used. Although
  2075. 2075                                the use of the Common Name is existing practice, it is deprecated and
  2076. 2076                                Certification Authorities are encouraged to use the dNSName instead. */
  2077. 2077                             if (preg_match('#^' $value '$#'$components['host'])) {
  2078. 2078                                 return true;
  2079. 2079                             }
  2080. 2080                             break;
  2081. 2081                         case 'iPAddress':
  2082. 2082                             /* From RFC2818 "HTTP over TLS":
  2083. 2083 
  2084. 2084                                In some cases, the URI is specified as an IP address rather than a
  2085. 2085                                hostname. In this case, the iPAddress subjectAltName must be present
  2086. 2086                                in the certificate and must exactly match the IP in the URI. */
  2087. 2087                             if (preg_match('#(?:\d{1-3}\.){4}#'$components['host'] . '.') && preg_match('#^' $value '$#'$components['host'])) {
  2088. 2088                                 return true;
  2089. 2089                             }
  2090. 2090                     }
  2091. 2091                 }
  2092. 2092             }
  2093. 2093             return false;
  2094. 2094         }
  2095. 2095 
  2096. 2096         if ($value $this->getDNProp('id-at-commonName')) {
  2097. 2097             $value str_replace(array('.''*'), array('\.''[^.]*'), $value[0]);
  2098. 2098             return preg_match('#^' $value '$#'$components['host']);
  2099. 2099         }
  2100. 2100 
  2101. 2101         return false;
  2102. 2102     }
  2103. 2103 
  2104. 2104     /**
  2105. 2105      * Validate a date
  2106. 2106      *
  2107. 2107      * If $date isn't defined it is assumed to be the current date.
  2108. 2108      *
  2109. 2109      * @param int $date optional
  2110. 2110      * @access public
  2111. 2111      */
  2112. 2112     function validateDate($date null)
  2113. 2113     {
  2114. 2114         if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
  2115. 2115             return false;
  2116. 2116         }
  2117. 2117 
  2118. 2118         if (!isset($date)) {
  2119. 2119             $date class_exists('DateTime') ?
  2120. 2120                 new DateTime($date, new DateTimeZone(@date_default_timezone_get())) :
  2121. 2121                 time();
  2122. 2122         }
  2123. 2123 
  2124. 2124         $notBefore $this->currentCert['tbsCertificate']['validity']['notBefore'];
  2125. 2125         $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime'];
  2126. 2126 
  2127. 2127         $notAfter $this->currentCert['tbsCertificate']['validity']['notAfter'];
  2128. 2128         $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
  2129. 2129 
  2130. 2130         if (class_exists('DateTime')) {
  2131. 2131             $notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get()));
  2132. 2132             $notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get()));
  2133. 2133         } else {
  2134. 2134             $notBefore = @strtotime($notBefore);
  2135. 2135             $notAfter = @strtotime($notAfter);
  2136. 2136         }
  2137. 2137 
  2138. 2138         switch (true) {
  2139. 2139             case $date $notBefore:
  2140. 2140             case $date $notAfter:
  2141. 2141                 return false;
  2142. 2142         }
  2143. 2143 
  2144. 2144         return true;
  2145. 2145     }
  2146. 2146 
  2147. 2147     /**
  2148. 2148      * Validate a signature
  2149. 2149      *
  2150. 2150      * Works on X.509 certs, CSR's and CRL's.
  2151. 2151      * Returns true if the signature is verified, false if it is not correct or null on error
  2152. 2152      *
  2153. 2153      * By default returns false for self-signed certs. Call validateSignature(false) to make this support
  2154. 2154      * self-signed.
  2155. 2155      *
  2156. 2156      * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
  2157. 2157      *
  2158. 2158      * @param bool $caonly optional
  2159. 2159      * @access public
  2160. 2160      * @return mixed
  2161. 2161      */
  2162. 2162     function validateSignature($caonly true)
  2163. 2163     {
  2164. 2164         if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
  2165. 2165             return null;
  2166. 2166         }
  2167. 2167 
  2168. 2168         /* TODO:
  2169. 2169            "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
  2170. 2170             -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
  2171. 2171 
  2172. 2172            implement pathLenConstraint in the id-ce-basicConstraints extension */
  2173. 2173 
  2174. 2174         switch (true) {
  2175. 2175             case isset($this->currentCert['tbsCertificate']):
  2176. 2176                 // self-signed cert
  2177. 2177                 switch (true) {
  2178. 2178                     case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']:
  2179. 2179                     case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(FILE_X509_DN_STRING) === $this->getDN(FILE_X509_DN_STRING):
  2180. 2180                         $authorityKey $this->getExtension('id-ce-authorityKeyIdentifier');
  2181. 2181                         $subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier');
  2182. 2182                         switch (true) {
  2183. 2183                             case !is_array($authorityKey):
  2184. 2184                             case !$subjectKeyID:
  2185. 2185                             case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
  2186. 2186                                 $signingCert $this->currentCert// working cert
  2187. 2187                         }
  2188. 2188                 }
  2189. 2189 
  2190. 2190                 if (!empty($this->CAs)) {
  2191. 2191                     for ($i 0$i count($this->CAs); $i++) {
  2192. 2192                         // even if the cert is a self-signed one we still want to see if it's a CA;
  2193. 2193                         // if not, we'll conditionally return an error
  2194. 2194                         $ca $this->CAs[$i];
  2195. 2195                         switch (true) {
  2196. 2196                             case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']:
  2197. 2197                             case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(FILE_X509_DN_STRING$this->currentCert['tbsCertificate']['issuer']) === $this->getDN(FILE_X509_DN_STRING$ca['tbsCertificate']['subject']):
  2198. 2198                                 $authorityKey $this->getExtension('id-ce-authorityKeyIdentifier');
  2199. 2199                                 $subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier'$ca);
  2200. 2200                                 switch (true) {
  2201. 2201                                     case !is_array($authorityKey):
  2202. 2202                                     case !$subjectKeyID:
  2203. 2203                                     case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
  2204. 2204                                         if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
  2205. 2205                                             break 2// serial mismatch - check other ca
  2206. 2206                                         }
  2207. 2207                                         $signingCert $ca// working cert
  2208. 2208                                         break 3;
  2209. 2209                                 }
  2210. 2210                         }
  2211. 2211                     }
  2212. 2212                     if (count($this->CAs) == $i && $caonly) {
  2213. 2213                         return false;
  2214. 2214                     }
  2215. 2215                 } elseif (!isset($signingCert) || $caonly) {
  2216. 2216                     return false;
  2217. 2217                 }
  2218. 2218                 return $this->_validateSignature(
  2219. 2219                     $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
  2220. 2220                     $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
  2221. 2221                     $this->currentCert['signatureAlgorithm']['algorithm'],
  2222. 2222                     substr(base64_decode($this->currentCert['signature']), 1),
  2223. 2223                     $this->signatureSubject
  2224. 2224                 );
  2225. 2225             case isset($this->currentCert['certificationRequestInfo']):
  2226. 2226                 return $this->_validateSignature(
  2227. 2227                     $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
  2228. 2228                     $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
  2229. 2229                     $this->currentCert['signatureAlgorithm']['algorithm'],
  2230. 2230                     substr(base64_decode($this->currentCert['signature']), 1),
  2231. 2231                     $this->signatureSubject
  2232. 2232                 );
  2233. 2233             case isset($this->currentCert['publicKeyAndChallenge']):
  2234. 2234                 return $this->_validateSignature(
  2235. 2235                     $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
  2236. 2236                     $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
  2237. 2237                     $this->currentCert['signatureAlgorithm']['algorithm'],
  2238. 2238                     substr(base64_decode($this->currentCert['signature']), 1),
  2239. 2239                     $this->signatureSubject
  2240. 2240                 );
  2241. 2241             case isset($this->currentCert['tbsCertList']):
  2242. 2242                 if (!empty($this->CAs)) {
  2243. 2243                     for ($i 0$i count($this->CAs); $i++) {
  2244. 2244                         $ca $this->CAs[$i];
  2245. 2245                         switch (true) {
  2246. 2246                             case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']:
  2247. 2247                             case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(FILE_X509_DN_STRING$this->currentCert['tbsCertList']['issuer']) === $this->getDN(FILE_X509_DN_STRING$ca['tbsCertificate']['subject']):
  2248. 2248                                 $authorityKey $this->getExtension('id-ce-authorityKeyIdentifier');
  2249. 2249                                 $subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier'$ca);
  2250. 2250                                 switch (true) {
  2251. 2251                                     case !is_array($authorityKey):
  2252. 2252                                     case !$subjectKeyID:
  2253. 2253                                     case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
  2254. 2254                                         if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
  2255. 2255                                             break 2// serial mismatch - check other ca
  2256. 2256                                         }
  2257. 2257                                         $signingCert $ca// working cert
  2258. 2258                                         break 3;
  2259. 2259                                 }
  2260. 2260                         }
  2261. 2261                     }
  2262. 2262                 }
  2263. 2263                 if (!isset($signingCert)) {
  2264. 2264                     return false;
  2265. 2265                 }
  2266. 2266                 return $this->_validateSignature(
  2267. 2267                     $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
  2268. 2268                     $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
  2269. 2269                     $this->currentCert['signatureAlgorithm']['algorithm'],
  2270. 2270                     substr(base64_decode($this->currentCert['signature']), 1),
  2271. 2271                     $this->signatureSubject
  2272. 2272                 );
  2273. 2273             default:
  2274. 2274                 return false;
  2275. 2275         }
  2276. 2276     }
  2277. 2277 
  2278. 2278     /**
  2279. 2279      * Validates a signature
  2280. 2280      *
  2281. 2281      * Returns true if the signature is verified, false if it is not correct or null on error
  2282. 2282      *
  2283. 2283      * @param string $publicKeyAlgorithm
  2284. 2284      * @param string $publicKey
  2285. 2285      * @param string $signatureAlgorithm
  2286. 2286      * @param string $signature
  2287. 2287      * @param string $signatureSubject
  2288. 2288      * @access private
  2289. 2289      * @return int
  2290. 2290      */
  2291. 2291     function _validateSignature($publicKeyAlgorithm$publicKey$signatureAlgorithm$signature$signatureSubject)
  2292. 2292     {
  2293. 2293         switch ($publicKeyAlgorithm) {
  2294. 2294             case 'rsaEncryption':
  2295. 2295                 if (!class_exists('Crypt_RSA')) {
  2296. 2296                     include_once 'Crypt/RSA.php';
  2297. 2297                 }
  2298. 2298                 $rsa = new Crypt_RSA();
  2299. 2299                 $rsa->loadKey($publicKey);
  2300. 2300 
  2301. 2301                 switch ($signatureAlgorithm) {
  2302. 2302                     case 'md2WithRSAEncryption':
  2303. 2303                     case 'md5WithRSAEncryption':
  2304. 2304                     case 'sha1WithRSAEncryption':
  2305. 2305                     case 'sha224WithRSAEncryption':
  2306. 2306                     case 'sha256WithRSAEncryption':
  2307. 2307                     case 'sha384WithRSAEncryption':
  2308. 2308                     case 'sha512WithRSAEncryption':
  2309. 2309                         $rsa->setHash(preg_replace('#WithRSAEncryption$#'''$signatureAlgorithm));
  2310. 2310                         $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  2311. 2311                         if (!@$rsa->verify($signatureSubject$signature)) {
  2312. 2312                             return false;
  2313. 2313                         }
  2314. 2314                         break;
  2315. 2315                     default:
  2316. 2316                         return null;
  2317. 2317                 }
  2318. 2318                 break;
  2319. 2319             default:
  2320. 2320                 return null;
  2321. 2321         }
  2322. 2322 
  2323. 2323         return true;
  2324. 2324     }
  2325. 2325 
  2326. 2326     /**
  2327. 2327      * Reformat public keys
  2328. 2328      *
  2329. 2329      * Reformats a public key to a format supported by phpseclib (if applicable)
  2330. 2330      *
  2331. 2331      * @param string $algorithm
  2332. 2332      * @param string $key
  2333. 2333      * @access private
  2334. 2334      * @return string
  2335. 2335      */
  2336. 2336     function _reformatKey($algorithm$key)
  2337. 2337     {
  2338. 2338         switch ($algorithm) {
  2339. 2339             case 'rsaEncryption':
  2340. 2340                 return
  2341. 2341                     "-----BEGIN RSA PUBLIC KEY-----\r\n" .
  2342. 2342                     // subjectPublicKey is stored as a bit string in X.509 certs.  the first byte of a bit string represents how many bits
  2343. 2343                     // in the last byte should be ignored.  the following only supports non-zero stuff but as none of the X.509 certs Firefox
  2344. 2344                     // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
  2345. 2345                     chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) .
  2346. 2346                     '-----END RSA PUBLIC KEY-----';
  2347. 2347             default:
  2348. 2348                 return $key;
  2349. 2349         }
  2350. 2350     }
  2351. 2351 
  2352. 2352     /**
  2353. 2353      * Decodes an IP address
  2354. 2354      *
  2355. 2355      * Takes in a base64 encoded "blob" and returns a human readable IP address
  2356. 2356      *
  2357. 2357      * @param string $ip
  2358. 2358      * @access private
  2359. 2359      * @return string
  2360. 2360      */
  2361. 2361     function _decodeIP($ip)
  2362. 2362     {
  2363. 2363         $ip base64_decode($ip);
  2364. 2364         list(, $ip) = unpack('N'$ip);
  2365. 2365         return long2ip($ip);
  2366. 2366     }
  2367. 2367 
  2368. 2368     /**
  2369. 2369      * Encodes an IP address
  2370. 2370      *
  2371. 2371      * Takes a human readable IP address into a base64-encoded "blob"
  2372. 2372      *
  2373. 2373      * @param string $ip
  2374. 2374      * @access private
  2375. 2375      * @return string
  2376. 2376      */
  2377. 2377     function _encodeIP($ip)
  2378. 2378     {
  2379. 2379         return base64_encode(pack('N'ip2long($ip)));
  2380. 2380     }
  2381. 2381 
  2382. 2382     /**
  2383. 2383      * "Normalizes" a Distinguished Name property
  2384. 2384      *
  2385. 2385      * @param string $propName
  2386. 2386      * @access private
  2387. 2387      * @return mixed
  2388. 2388      */
  2389. 2389     function _translateDNProp($propName)
  2390. 2390     {
  2391. 2391         switch (strtolower($propName)) {
  2392. 2392             case 'id-at-countryname':
  2393. 2393             case 'countryname':
  2394. 2394             case 'c':
  2395. 2395                 return 'id-at-countryName';
  2396. 2396             case 'id-at-organizationname':
  2397. 2397             case 'organizationname':
  2398. 2398             case 'o':
  2399. 2399                 return 'id-at-organizationName';
  2400. 2400             case 'id-at-dnqualifier':
  2401. 2401             case 'dnqualifier':
  2402. 2402                 return 'id-at-dnQualifier';
  2403. 2403             case 'id-at-commonname':
  2404. 2404             case 'commonname':
  2405. 2405             case 'cn':
  2406. 2406                 return 'id-at-commonName';
  2407. 2407             case 'id-at-stateorprovincename':
  2408. 2408             case 'stateorprovincename':
  2409. 2409             case 'state':
  2410. 2410             case 'province':
  2411. 2411             case 'provincename':
  2412. 2412             case 'st':
  2413. 2413                 return 'id-at-stateOrProvinceName';
  2414. 2414             case 'id-at-localityname':
  2415. 2415             case 'localityname':
  2416. 2416             case 'l':
  2417. 2417                 return 'id-at-localityName';
  2418. 2418             case 'id-emailaddress':
  2419. 2419             case 'emailaddress':
  2420. 2420                 return 'pkcs-9-at-emailAddress';
  2421. 2421             case 'id-at-serialnumber':
  2422. 2422             case 'serialnumber':
  2423. 2423                 return 'id-at-serialNumber';
  2424. 2424             case 'id-at-postalcode':
  2425. 2425             case 'postalcode':
  2426. 2426                 return 'id-at-postalCode';
  2427. 2427             case 'id-at-streetaddress':
  2428. 2428             case 'streetaddress':
  2429. 2429                 return 'id-at-streetAddress';
  2430. 2430             case 'id-at-name':
  2431. 2431             case 'name':
  2432. 2432                 return 'id-at-name';
  2433. 2433             case 'id-at-givenname':
  2434. 2434             case 'givenname':
  2435. 2435                 return 'id-at-givenName';
  2436. 2436             case 'id-at-surname':
  2437. 2437             case 'surname':
  2438. 2438             case 'sn':
  2439. 2439                 return 'id-at-surname';
  2440. 2440             case 'id-at-initials':
  2441. 2441             case 'initials':
  2442. 2442                 return 'id-at-initials';
  2443. 2443             case 'id-at-generationqualifier':
  2444. 2444             case 'generationqualifier':
  2445. 2445                 return 'id-at-generationQualifier';
  2446. 2446             case 'id-at-organizationalunitname':
  2447. 2447             case 'organizationalunitname':
  2448. 2448             case 'ou':
  2449. 2449                 return 'id-at-organizationalUnitName';
  2450. 2450             case 'id-at-pseudonym':
  2451. 2451             case 'pseudonym':
  2452. 2452                 return 'id-at-pseudonym';
  2453. 2453             case 'id-at-title':
  2454. 2454             case 'title':
  2455. 2455                 return 'id-at-title';
  2456. 2456             case 'id-at-description':
  2457. 2457             case 'description':
  2458. 2458                 return 'id-at-description';
  2459. 2459             case 'id-at-role':
  2460. 2460             case 'role':
  2461. 2461                 return 'id-at-role';
  2462. 2462             case 'id-at-uniqueidentifier':
  2463. 2463             case 'uniqueidentifier':
  2464. 2464             case 'x500uniqueidentifier':
  2465. 2465                 return 'id-at-uniqueIdentifier';
  2466. 2466             case 'postaladdress':
  2467. 2467             case 'id-at-postaladdress':
  2468. 2468                 return 'id-at-postalAddress';
  2469. 2469             default:
  2470. 2470                 return false;
  2471. 2471         }
  2472. 2472     }
  2473. 2473 
  2474. 2474     /**
  2475. 2475      * Set a Distinguished Name property
  2476. 2476      *
  2477. 2477      * @param string $propName
  2478. 2478      * @param mixed $propValue
  2479. 2479      * @param string $type optional
  2480. 2480      * @access public
  2481. 2481      * @return bool
  2482. 2482      */
  2483. 2483     function setDNProp($propName$propValue$type 'utf8String')
  2484. 2484     {
  2485. 2485         if (empty($this->dn)) {
  2486. 2486             $this->dn = array('rdnSequence' => array());
  2487. 2487         }
  2488. 2488 
  2489. 2489         if (($propName $this->_translateDNProp($propName)) === false) {
  2490. 2490             return false;
  2491. 2491         }
  2492. 2492 
  2493. 2493         foreach ((array) $propValue as $v) {
  2494. 2494             if (!is_array($v) && isset($type)) {
  2495. 2495                 $v = array($type => $v);
  2496. 2496             }
  2497. 2497             $this->dn['rdnSequence'][] = array(
  2498. 2498                 array(
  2499. 2499                     'type' => $propName,
  2500. 2500                     'value'=> $v
  2501. 2501                 )
  2502. 2502             );
  2503. 2503         }
  2504. 2504 
  2505. 2505         return true;
  2506. 2506     }
  2507. 2507 
  2508. 2508     /**
  2509. 2509      * Remove Distinguished Name properties
  2510. 2510      *
  2511. 2511      * @param string $propName
  2512. 2512      * @access public
  2513. 2513      */
  2514. 2514     function removeDNProp($propName)
  2515. 2515     {
  2516. 2516         if (empty($this->dn)) {
  2517. 2517             return;
  2518. 2518         }
  2519. 2519 
  2520. 2520         if (($propName $this->_translateDNProp($propName)) === false) {
  2521. 2521             return;
  2522. 2522         }
  2523. 2523 
  2524. 2524         $dn = &$this->dn['rdnSequence'];
  2525. 2525         $size count($dn);
  2526. 2526         for ($i 0$i $size$i++) {
  2527. 2527             if ($dn[$i][0]['type'] == $propName) {
  2528. 2528                 unset($dn[$i]);
  2529. 2529             }
  2530. 2530         }
  2531. 2531 
  2532. 2532         $dn array_values($dn);
  2533. 2533         // fix for https://bugs.php.net/75433 affecting PHP 7.2
  2534. 2534         if (!isset($dn[0])) {
  2535. 2535             $dn array_splice($dn00);
  2536. 2536         }
  2537. 2537     }
  2538. 2538 
  2539. 2539     /**
  2540. 2540      * Get Distinguished Name properties
  2541. 2541      *
  2542. 2542      * @param string $propName
  2543. 2543      * @param array $dn optional
  2544. 2544      * @param bool $withType optional
  2545. 2545      * @return mixed
  2546. 2546      * @access public
  2547. 2547      */
  2548. 2548     function getDNProp($propName$dn null$withType false)
  2549. 2549     {
  2550. 2550         if (!isset($dn)) {
  2551. 2551             $dn $this->dn;
  2552. 2552         }
  2553. 2553 
  2554. 2554         if (empty($dn)) {
  2555. 2555             return false;
  2556. 2556         }
  2557. 2557 
  2558. 2558         if (($propName $this->_translateDNProp($propName)) === false) {
  2559. 2559             return false;
  2560. 2560         }
  2561. 2561 
  2562. 2562         $asn1 = new File_ASN1();
  2563. 2563         $asn1->loadOIDs($this->oids);
  2564. 2564         $filters = array();
  2565. 2565         $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  2566. 2566         $asn1->loadFilters($filters);
  2567. 2567         $this->_mapOutDNs($dn'rdnSequence'$asn1);
  2568. 2568         $dn $dn['rdnSequence'];
  2569. 2569         $result = array();
  2570. 2570         for ($i 0$i count($dn); $i++) {
  2571. 2571             if ($dn[$i][0]['type'] == $propName) {
  2572. 2572                 $v $dn[$i][0]['value'];
  2573. 2573                 if (!$withType) {
  2574. 2574                     if (is_array($v)) {
  2575. 2575                         foreach ($v as $type => $s) {
  2576. 2576                             $type array_search($type$asn1->ANYmaptrue);
  2577. 2577                             if ($type !== false && isset($asn1->stringTypeSize[$type])) {
  2578. 2578                                 $s $asn1->convert($s$type);
  2579. 2579                                 if ($s !== false) {
  2580. 2580                                     $v $s;
  2581. 2581                                     break;
  2582. 2582                                 }
  2583. 2583                             }
  2584. 2584                         }
  2585. 2585                         if (is_array($v)) {
  2586. 2586                             $v array_pop($v); // Always strip data type.
  2587. 2587                         }
  2588. 2588                     } elseif (is_object($v) && strtolower(get_class($v)) == 'file_asn1_element') {
  2589. 2589                         $map $this->_getMapping($propName);
  2590. 2590                         if (!is_bool($map)) {
  2591. 2591                             $decoded $asn1->decodeBER($v);
  2592. 2592                             $v $asn1->asn1map($decoded[0], $map);
  2593. 2593                         }
  2594. 2594                     }
  2595. 2595                 }
  2596. 2596                 $result[] = $v;
  2597. 2597             }
  2598. 2598         }
  2599. 2599 
  2600. 2600         return $result;
  2601. 2601     }
  2602. 2602 
  2603. 2603     /**
  2604. 2604      * Set a Distinguished Name
  2605. 2605      *
  2606. 2606      * @param mixed $dn
  2607. 2607      * @param bool $merge optional
  2608. 2608      * @param string $type optional
  2609. 2609      * @access public
  2610. 2610      * @return bool
  2611. 2611      */
  2612. 2612     function setDN($dn$merge false$type 'utf8String')
  2613. 2613     {
  2614. 2614         if (!$merge) {
  2615. 2615             $this->dn null;
  2616. 2616         }
  2617. 2617 
  2618. 2618         if (is_array($dn)) {
  2619. 2619             if (isset($dn['rdnSequence'])) {
  2620. 2620                 $this->dn $dn// No merge here.
  2621. 2621                 return true;
  2622. 2622             }
  2623. 2623 
  2624. 2624             // handles stuff generated by openssl_x509_parse()
  2625. 2625             foreach ($dn as $prop => $value) {
  2626. 2626                 if (!$this->setDNProp($prop$value$type)) {
  2627. 2627                     return false;
  2628. 2628                 }
  2629. 2629             }
  2630. 2630             return true;
  2631. 2631         }
  2632. 2632 
  2633. 2633         // handles everything else
  2634. 2634         $results preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#'$dn, -1PREG_SPLIT_DELIM_CAPTURE);
  2635. 2635         for ($i 1$i count($results); $i+=2) {
  2636. 2636             $prop trim($results[$i], ', =/');
  2637. 2637             $value $results[$i 1];
  2638. 2638             if (!$this->setDNProp($prop$value$type)) {
  2639. 2639                 return false;
  2640. 2640             }
  2641. 2641         }
  2642. 2642 
  2643. 2643         return true;
  2644. 2644     }
  2645. 2645 
  2646. 2646     /**
  2647. 2647      * Get the Distinguished Name for a certificates subject
  2648. 2648      *
  2649. 2649      * @param mixed $format optional
  2650. 2650      * @param array $dn optional
  2651. 2651      * @access public
  2652. 2652      * @return bool
  2653. 2653      */
  2654. 2654     function getDN($format FILE_X509_DN_ARRAY$dn null)
  2655. 2655     {
  2656. 2656         if (!isset($dn)) {
  2657. 2657             $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
  2658. 2658         }
  2659. 2659 
  2660. 2660         switch ((int) $format) {
  2661. 2661             case FILE_X509_DN_ARRAY:
  2662. 2662                 return $dn;
  2663. 2663             case FILE_X509_DN_ASN1:
  2664. 2664                 $asn1 = new File_ASN1();
  2665. 2665                 $asn1->loadOIDs($this->oids);
  2666. 2666                 $filters = array();
  2667. 2667                 $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  2668. 2668                 $asn1->loadFilters($filters);
  2669. 2669                 $this->_mapOutDNs($dn'rdnSequence'$asn1);
  2670. 2670                 return $asn1->encodeDER($dn$this->Name);
  2671. 2671             case FILE_X509_DN_CANON:
  2672. 2672                 //  No SEQUENCE around RDNs and all string values normalized as
  2673. 2673                 // trimmed lowercase UTF-8 with all spacing as one blank.
  2674. 2674                 // constructed RDNs will not be canonicalized
  2675. 2675                 $asn1 = new File_ASN1();
  2676. 2676                 $asn1->loadOIDs($this->oids);
  2677. 2677                 $filters = array();
  2678. 2678                 $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  2679. 2679                 $asn1->loadFilters($filters);
  2680. 2680                 $result '';
  2681. 2681                 $this->_mapOutDNs($dn'rdnSequence'$asn1);
  2682. 2682                 foreach ($dn['rdnSequence'] as $rdn) {
  2683. 2683                     foreach ($rdn as $i => $attr) {
  2684. 2684                         $attr = &$rdn[$i];
  2685. 2685                         if (is_array($attr['value'])) {
  2686. 2686                             foreach ($attr['value'] as $type => $v) {
  2687. 2687                                 $type array_search($type$asn1->ANYmaptrue);
  2688. 2688                                 if ($type !== false && isset($asn1->stringTypeSize[$type])) {
  2689. 2689                                     $v $asn1->convert($v$type);
  2690. 2690                                     if ($v !== false) {
  2691. 2691                                         $v preg_replace('/\s+/'' '$v);
  2692. 2692                                         $attr['value'] = strtolower(trim($v));
  2693. 2693                                         break;
  2694. 2694                                     }
  2695. 2695                                 }
  2696. 2696                             }
  2697. 2697                         }
  2698. 2698                     }
  2699. 2699                     $result .= $asn1->encodeDER($rdn$this->RelativeDistinguishedName);
  2700. 2700                 }
  2701. 2701                 return $result;
  2702. 2702             case FILE_X509_DN_HASH:
  2703. 2703                 $dn $this->getDN(FILE_X509_DN_CANON$dn);
  2704. 2704                 if (!class_exists('Crypt_Hash')) {
  2705. 2705                     include_once 'Crypt/Hash.php';
  2706. 2706                 }
  2707. 2707                 $hash = new Crypt_Hash('sha1');
  2708. 2708                 $hash $hash->hash($dn);
  2709. 2709                 extract(unpack('Vhash'$hash));
  2710. 2710                 return strtolower(bin2hex(pack('N'$hash)));
  2711. 2711         }
  2712. 2712 
  2713. 2713         // Default is to return a string.
  2714. 2714         $start true;
  2715. 2715         $output '';
  2716. 2716         $result = array();
  2717. 2717         $asn1 = new File_ASN1();
  2718. 2718         $asn1->loadOIDs($this->oids);
  2719. 2719         $filters = array();
  2720. 2720         $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  2721. 2721         $asn1->loadFilters($filters);
  2722. 2722         $this->_mapOutDNs($dn'rdnSequence'$asn1);
  2723. 2723         foreach ($dn['rdnSequence'] as $field) {
  2724. 2724             $prop $field[0]['type'];
  2725. 2725             $value $field[0]['value'];
  2726. 2726 
  2727. 2727             $delim ', ';
  2728. 2728             switch ($prop) {
  2729. 2729                 case 'id-at-countryName':
  2730. 2730                     $desc 'C';
  2731. 2731                     break;
  2732. 2732                 case 'id-at-stateOrProvinceName':
  2733. 2733                     $desc 'ST';
  2734. 2734                     break;
  2735. 2735                 case 'id-at-organizationName':
  2736. 2736                     $desc 'O';
  2737. 2737                     break;
  2738. 2738                 case 'id-at-organizationalUnitName':
  2739. 2739                     $desc 'OU';
  2740. 2740                     break;
  2741. 2741                 case 'id-at-commonName':
  2742. 2742                     $desc 'CN';
  2743. 2743                     break;
  2744. 2744                 case 'id-at-localityName':
  2745. 2745                     $desc 'L';
  2746. 2746                     break;
  2747. 2747                 case 'id-at-surname':
  2748. 2748                     $desc 'SN';
  2749. 2749                     break;
  2750. 2750                 case 'id-at-uniqueIdentifier':
  2751. 2751                     $delim '/';
  2752. 2752                     $desc 'x500UniqueIdentifier';
  2753. 2753                     break;
  2754. 2754                 case 'id-at-postalAddress':
  2755. 2755                     $delim '/';
  2756. 2756                     $desc 'postalAddress';
  2757. 2757                     break;
  2758. 2758                 default:
  2759. 2759                     $delim '/';
  2760. 2760                     $desc preg_replace('#.+-([^-]+)$#''$1'$prop);
  2761. 2761             }
  2762. 2762 
  2763. 2763             if (!$start) {
  2764. 2764                 $output.= $delim;
  2765. 2765             }
  2766. 2766             if (is_array($value)) {
  2767. 2767                 foreach ($value as $type => $v) {
  2768. 2768                     $type array_search($type$asn1->ANYmaptrue);
  2769. 2769                     if ($type !== false && isset($asn1->stringTypeSize[$type])) {
  2770. 2770                         $v $asn1->convert($v$type);
  2771. 2771                         if ($v !== false) {
  2772. 2772                             $value $v;
  2773. 2773                             break;
  2774. 2774                         }
  2775. 2775                     }
  2776. 2776                 }
  2777. 2777                 if (is_array($value)) {
  2778. 2778                     $value array_pop($value); // Always strip data type.
  2779. 2779                 }
  2780. 2780             } elseif (is_object($value) && strtolower(get_class($value)) == 'file_asn1_element') {
  2781. 2781                 // @codingStandardsIgnoreStart
  2782. 2782                 $callback version_compare(PHP_VERSION'5.3.0') >= ?
  2783. 2783                     function ($x) { return "\x" bin2hex($x[0]); } :
  2784. 2784                     create_function('$x''return "\x" . bin2hex($x[0]);');
  2785. 2785                 // @codingStandardsIgnoreEnd
  2786. 2786                 $value strtoupper(preg_replace_callback('#[^\x20-\x7E]#'$callback$value->element));
  2787. 2787             }
  2788. 2788             $output.= $desc '=' $value;
  2789. 2789             $result[$desc] = isset($result[$desc]) ?
  2790. 2790                 array_merge((array) $dn[$prop], array($value)) :
  2791. 2791                 $value;
  2792. 2792             $start false;
  2793. 2793         }
  2794. 2794 
  2795. 2795         return $format == FILE_X509_DN_OPENSSL $result $output;
  2796. 2796     }
  2797. 2797 
  2798. 2798     /**
  2799. 2799      * Get the Distinguished Name for a certificate/crl issuer
  2800. 2800      *
  2801. 2801      * @param int $format optional
  2802. 2802      * @access public
  2803. 2803      * @return mixed
  2804. 2804      */
  2805. 2805     function getIssuerDN($format FILE_X509_DN_ARRAY)
  2806. 2806     {
  2807. 2807         switch (true) {
  2808. 2808             case !isset($this->currentCert) || !is_array($this->currentCert):
  2809. 2809                 break;
  2810. 2810             case isset($this->currentCert['tbsCertificate']):
  2811. 2811                 return $this->getDN($format$this->currentCert['tbsCertificate']['issuer']);
  2812. 2812             case isset($this->currentCert['tbsCertList']):
  2813. 2813                 return $this->getDN($format$this->currentCert['tbsCertList']['issuer']);
  2814. 2814         }
  2815. 2815 
  2816. 2816         return false;
  2817. 2817     }
  2818. 2818 
  2819. 2819     /**
  2820. 2820      * Get the Distinguished Name for a certificate/csr subject
  2821. 2821      * Alias of getDN()
  2822. 2822      *
  2823. 2823      * @param int $format optional
  2824. 2824      * @access public
  2825. 2825      * @return mixed
  2826. 2826      */
  2827. 2827     function getSubjectDN($format FILE_X509_DN_ARRAY)
  2828. 2828     {
  2829. 2829         switch (true) {
  2830. 2830             case !empty($this->dn):
  2831. 2831                 return $this->getDN($format);
  2832. 2832             case !isset($this->currentCert) || !is_array($this->currentCert):
  2833. 2833                 break;
  2834. 2834             case isset($this->currentCert['tbsCertificate']):
  2835. 2835                 return $this->getDN($format$this->currentCert['tbsCertificate']['subject']);
  2836. 2836             case isset($this->currentCert['certificationRequestInfo']):
  2837. 2837                 return $this->getDN($format$this->currentCert['certificationRequestInfo']['subject']);
  2838. 2838         }
  2839. 2839 
  2840. 2840         return false;
  2841. 2841     }
  2842. 2842 
  2843. 2843     /**
  2844. 2844      * Get an individual Distinguished Name property for a certificate/crl issuer
  2845. 2845      *
  2846. 2846      * @param string $propName
  2847. 2847      * @param bool $withType optional
  2848. 2848      * @access public
  2849. 2849      * @return mixed
  2850. 2850      */
  2851. 2851     function getIssuerDNProp($propName$withType false)
  2852. 2852     {
  2853. 2853         switch (true) {
  2854. 2854             case !isset($this->currentCert) || !is_array($this->currentCert):
  2855. 2855                 break;
  2856. 2856             case isset($this->currentCert['tbsCertificate']):
  2857. 2857                 return $this->getDNProp($propName$this->currentCert['tbsCertificate']['issuer'], $withType);
  2858. 2858             case isset($this->currentCert['tbsCertList']):
  2859. 2859                 return $this->getDNProp($propName$this->currentCert['tbsCertList']['issuer'], $withType);
  2860. 2860         }
  2861. 2861 
  2862. 2862         return false;
  2863. 2863     }
  2864. 2864 
  2865. 2865     /**
  2866. 2866      * Get an individual Distinguished Name property for a certificate/csr subject
  2867. 2867      *
  2868. 2868      * @param string $propName
  2869. 2869      * @param bool $withType optional
  2870. 2870      * @access public
  2871. 2871      * @return mixed
  2872. 2872      */
  2873. 2873     function getSubjectDNProp($propName$withType false)
  2874. 2874     {
  2875. 2875         switch (true) {
  2876. 2876             case !empty($this->dn):
  2877. 2877                 return $this->getDNProp($propNamenull$withType);
  2878. 2878             case !isset($this->currentCert) || !is_array($this->currentCert):
  2879. 2879                 break;
  2880. 2880             case isset($this->currentCert['tbsCertificate']):
  2881. 2881                 return $this->getDNProp($propName$this->currentCert['tbsCertificate']['subject'], $withType);
  2882. 2882             case isset($this->currentCert['certificationRequestInfo']):
  2883. 2883                 return $this->getDNProp($propName$this->currentCert['certificationRequestInfo']['subject'], $withType);
  2884. 2884         }
  2885. 2885 
  2886. 2886         return false;
  2887. 2887     }
  2888. 2888 
  2889. 2889     /**
  2890. 2890      * Get the certificate chain for the current cert
  2891. 2891      *
  2892. 2892      * @access public
  2893. 2893      * @return mixed
  2894. 2894      */
  2895. 2895     function getChain()
  2896. 2896     {
  2897. 2897         $chain = array($this->currentCert);
  2898. 2898 
  2899. 2899         if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
  2900. 2900             return false;
  2901. 2901         }
  2902. 2902         if (empty($this->CAs)) {
  2903. 2903             return $chain;
  2904. 2904         }
  2905. 2905         while (true) {
  2906. 2906             $currentCert $chain[count($chain) - 1];
  2907. 2907             for ($i 0$i count($this->CAs); $i++) {
  2908. 2908                 $ca $this->CAs[$i];
  2909. 2909                 if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
  2910. 2910                     $authorityKey $this->getExtension('id-ce-authorityKeyIdentifier'$currentCert);
  2911. 2911                     $subjectKeyID $this->getExtension('id-ce-subjectKeyIdentifier'$ca);
  2912. 2912                     switch (true) {
  2913. 2913                         case !is_array($authorityKey):
  2914. 2914                         case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
  2915. 2915                             if ($currentCert === $ca) {
  2916. 2916                                 break 3;
  2917. 2917                             }
  2918. 2918                             $chain[] = $ca;
  2919. 2919                             break 2;
  2920. 2920                     }
  2921. 2921                 }
  2922. 2922             }
  2923. 2923             if ($i == count($this->CAs)) {
  2924. 2924                 break;
  2925. 2925             }
  2926. 2926         }
  2927. 2927         foreach ($chain as $key => $value) {
  2928. 2928             $chain[$key] = new File_X509();
  2929. 2929             $chain[$key]->loadX509($value);
  2930. 2930         }
  2931. 2931         return $chain;
  2932. 2932     }
  2933. 2933 
  2934. 2934     /**
  2935. 2935      * Set public key
  2936. 2936      *
  2937. 2937      * Key needs to be a Crypt_RSA object
  2938. 2938      *
  2939. 2939      * @param object $key
  2940. 2940      * @access public
  2941. 2941      * @return bool
  2942. 2942      */
  2943. 2943     function setPublicKey($key)
  2944. 2944     {
  2945. 2945         $key->setPublicKey();
  2946. 2946         $this->publicKey $key;
  2947. 2947     }
  2948. 2948 
  2949. 2949     /**
  2950. 2950      * Set private key
  2951. 2951      *
  2952. 2952      * Key needs to be a Crypt_RSA object
  2953. 2953      *
  2954. 2954      * @param object $key
  2955. 2955      * @access public
  2956. 2956      */
  2957. 2957     function setPrivateKey($key)
  2958. 2958     {
  2959. 2959         $this->privateKey $key;
  2960. 2960     }
  2961. 2961 
  2962. 2962     /**
  2963. 2963      * Set challenge
  2964. 2964      *
  2965. 2965      * Used for SPKAC CSR's
  2966. 2966      *
  2967. 2967      * @param string $challenge
  2968. 2968      * @access public
  2969. 2969      */
  2970. 2970     function setChallenge($challenge)
  2971. 2971     {
  2972. 2972         $this->challenge $challenge;
  2973. 2973     }
  2974. 2974 
  2975. 2975     /**
  2976. 2976      * Gets the public key
  2977. 2977      *
  2978. 2978      * Returns a Crypt_RSA object or a false.
  2979. 2979      *
  2980. 2980      * @access public
  2981. 2981      * @return mixed
  2982. 2982      */
  2983. 2983     function getPublicKey()
  2984. 2984     {
  2985. 2985         if (isset($this->publicKey)) {
  2986. 2986             return $this->publicKey;
  2987. 2987         }
  2988. 2988 
  2989. 2989         if (isset($this->currentCert) && is_array($this->currentCert)) {
  2990. 2990             foreach (array('tbsCertificate/subjectPublicKeyInfo''certificationRequestInfo/subjectPKInfo') as $path) {
  2991. 2991                 $keyinfo $this->_subArray($this->currentCert$path);
  2992. 2992                 if (!empty($keyinfo)) {
  2993. 2993                     break;
  2994. 2994                 }
  2995. 2995             }
  2996. 2996         }
  2997. 2997         if (empty($keyinfo)) {
  2998. 2998             return false;
  2999. 2999         }
  3000. 3000 
  3001. 3001         $key $keyinfo['subjectPublicKey'];
  3002. 3002 
  3003. 3003         switch ($keyinfo['algorithm']['algorithm']) {
  3004. 3004             case 'rsaEncryption':
  3005. 3005                 if (!class_exists('Crypt_RSA')) {
  3006. 3006                     include_once 'Crypt/RSA.php';
  3007. 3007                 }
  3008. 3008                 $publicKey = new Crypt_RSA();
  3009. 3009                 $publicKey->loadKey($key);
  3010. 3010                 $publicKey->setPublicKey();
  3011. 3011                 break;
  3012. 3012             default:
  3013. 3013                 return false;
  3014. 3014         }
  3015. 3015 
  3016. 3016         return $publicKey;
  3017. 3017     }
  3018. 3018 
  3019. 3019     /**
  3020. 3020      * Load a Certificate Signing Request
  3021. 3021      *
  3022. 3022      * @param string $csr
  3023. 3023      * @access public
  3024. 3024      * @return mixed
  3025. 3025      */
  3026. 3026     function loadCSR($csr$mode FILE_X509_FORMAT_AUTO_DETECT)
  3027. 3027     {
  3028. 3028         if (is_array($csr) && isset($csr['certificationRequestInfo'])) {
  3029. 3029             unset($this->currentCert);
  3030. 3030             unset($this->currentKeyIdentifier);
  3031. 3031             unset($this->signatureSubject);
  3032. 3032             $this->dn $csr['certificationRequestInfo']['subject'];
  3033. 3033             if (!isset($this->dn)) {
  3034. 3034                 return false;
  3035. 3035             }
  3036. 3036 
  3037. 3037             $this->currentCert $csr;
  3038. 3038             return $csr;
  3039. 3039         }
  3040. 3040 
  3041. 3041         // see http://tools.ietf.org/html/rfc2986
  3042. 3042 
  3043. 3043         $asn1 = new File_ASN1();
  3044. 3044 
  3045. 3045         if ($mode != FILE_X509_FORMAT_DER) {
  3046. 3046             $newcsr $this->_extractBER($csr);
  3047. 3047             if ($mode == FILE_X509_FORMAT_PEM && $csr == $newcsr) {
  3048. 3048                 return false;
  3049. 3049             }
  3050. 3050             $csr $newcsr;
  3051. 3051         }
  3052. 3052         $orig $csr;
  3053. 3053 
  3054. 3054         if ($csr === false) {
  3055. 3055             $this->currentCert false;
  3056. 3056             return false;
  3057. 3057         }
  3058. 3058 
  3059. 3059         $asn1->loadOIDs($this->oids);
  3060. 3060         $decoded $asn1->decodeBER($csr);
  3061. 3061 
  3062. 3062         if (empty($decoded)) {
  3063. 3063             $this->currentCert false;
  3064. 3064             return false;
  3065. 3065         }
  3066. 3066 
  3067. 3067         $csr $asn1->asn1map($decoded[0], $this->CertificationRequest);
  3068. 3068         if (!isset($csr) || $csr === false) {
  3069. 3069             $this->currentCert false;
  3070. 3070             return false;
  3071. 3071         }
  3072. 3072 
  3073. 3073         $this->_mapInAttributes($csr'certificationRequestInfo/attributes'$asn1);
  3074. 3074         $this->_mapInDNs($csr'certificationRequestInfo/subject/rdnSequence'$asn1);
  3075. 3075 
  3076. 3076         $this->dn $csr['certificationRequestInfo']['subject'];
  3077. 3077 
  3078. 3078         $this->signatureSubject substr($orig$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
  3079. 3079 
  3080. 3080         $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
  3081. 3081         $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
  3082. 3082         $key $this->_reformatKey($algorithm$key);
  3083. 3083 
  3084. 3084         switch ($algorithm) {
  3085. 3085             case 'rsaEncryption':
  3086. 3086                 if (!class_exists('Crypt_RSA')) {
  3087. 3087                     include_once 'Crypt/RSA.php';
  3088. 3088                 }
  3089. 3089                 $this->publicKey = new Crypt_RSA();
  3090. 3090                 $this->publicKey->loadKey($key);
  3091. 3091                 $this->publicKey->setPublicKey();
  3092. 3092                 break;
  3093. 3093             default:
  3094. 3094                 $this->publicKey null;
  3095. 3095         }
  3096. 3096 
  3097. 3097         $this->currentKeyIdentifier null;
  3098. 3098         $this->currentCert $csr;
  3099. 3099 
  3100. 3100         return $csr;
  3101. 3101     }
  3102. 3102 
  3103. 3103     /**
  3104. 3104      * Save CSR request
  3105. 3105      *
  3106. 3106      * @param array $csr
  3107. 3107      * @param int $format optional
  3108. 3108      * @access public
  3109. 3109      * @return string
  3110. 3110      */
  3111. 3111     function saveCSR($csr$format FILE_X509_FORMAT_PEM)
  3112. 3112     {
  3113. 3113         if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
  3114. 3114             return false;
  3115. 3115         }
  3116. 3116 
  3117. 3117         switch (true) {
  3118. 3118             case !($algorithm $this->_subArray($csr'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
  3119. 3119             case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
  3120. 3120                 break;
  3121. 3121             default:
  3122. 3122                 switch ($algorithm) {
  3123. 3123                     case 'rsaEncryption':
  3124. 3124                         $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
  3125. 3125                             = base64_encode("\0" base64_decode(preg_replace('#-.+-|[\r\n]#'''$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
  3126. 3126                         $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null;
  3127. 3127                         $csr['signatureAlgorithm']['parameters'] = null;
  3128. 3128                         $csr['certificationRequestInfo']['signature']['parameters'] = null;
  3129. 3129                 }
  3130. 3130         }
  3131. 3131 
  3132. 3132         $asn1 = new File_ASN1();
  3133. 3133 
  3134. 3134         $asn1->loadOIDs($this->oids);
  3135. 3135 
  3136. 3136         $filters = array();
  3137. 3137         $filters['certificationRequestInfo']['subject']['rdnSequence']['value']
  3138. 3138             = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  3139. 3139 
  3140. 3140         $asn1->loadFilters($filters);
  3141. 3141 
  3142. 3142         $this->_mapOutDNs($csr'certificationRequestInfo/subject/rdnSequence'$asn1);
  3143. 3143         $this->_mapOutAttributes($csr'certificationRequestInfo/attributes'$asn1);
  3144. 3144         $csr $asn1->encodeDER($csr$this->CertificationRequest);
  3145. 3145 
  3146. 3146         switch ($format) {
  3147. 3147             case FILE_X509_FORMAT_DER:
  3148. 3148                 return $csr;
  3149. 3149             // case FILE_X509_FORMAT_PEM:
  3150. 3150             default:
  3151. 3151                 return "-----BEGIN CERTIFICATE REQUEST-----\r\n" chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----';
  3152. 3152         }
  3153. 3153     }
  3154. 3154 
  3155. 3155     /**
  3156. 3156      * Load a SPKAC CSR
  3157. 3157      *
  3158. 3158      * SPKAC's are produced by the HTML5 keygen element:
  3159. 3159      *
  3160. 3160      * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
  3161. 3161      *
  3162. 3162      * @param string $csr
  3163. 3163      * @access public
  3164. 3164      * @return mixed
  3165. 3165      */
  3166. 3166     function loadSPKAC($spkac)
  3167. 3167     {
  3168. 3168         if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) {
  3169. 3169             unset($this->currentCert);
  3170. 3170             unset($this->currentKeyIdentifier);
  3171. 3171             unset($this->signatureSubject);
  3172. 3172             $this->currentCert $spkac;
  3173. 3173             return $spkac;
  3174. 3174         }
  3175. 3175 
  3176. 3176         // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge
  3177. 3177 
  3178. 3178         $asn1 = new File_ASN1();
  3179. 3179 
  3180. 3180         // OpenSSL produces SPKAC's that are preceded by the string SPKAC=
  3181. 3181         $temp preg_replace('#(?:SPKAC=)|[ \r\n\\\]#'''$spkac);
  3182. 3182         $temp preg_match('#^[a-zA-Z\d/+]*={0,2}$#'$temp) ? base64_decode($temp) : false;
  3183. 3183         if ($temp != false) {
  3184. 3184             $spkac $temp;
  3185. 3185         }
  3186. 3186         $orig $spkac;
  3187. 3187 
  3188. 3188         if ($spkac === false) {
  3189. 3189             $this->currentCert false;
  3190. 3190             return false;
  3191. 3191         }
  3192. 3192 
  3193. 3193         $asn1->loadOIDs($this->oids);
  3194. 3194         $decoded $asn1->decodeBER($spkac);
  3195. 3195 
  3196. 3196         if (empty($decoded)) {
  3197. 3197             $this->currentCert false;
  3198. 3198             return false;
  3199. 3199         }
  3200. 3200 
  3201. 3201         $spkac $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge);
  3202. 3202 
  3203. 3203         if (!isset($spkac) || $spkac === false) {
  3204. 3204             $this->currentCert false;
  3205. 3205             return false;
  3206. 3206         }
  3207. 3207 
  3208. 3208         $this->signatureSubject substr($orig$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
  3209. 3209 
  3210. 3210         $algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
  3211. 3211         $key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
  3212. 3212         $key $this->_reformatKey($algorithm$key);
  3213. 3213 
  3214. 3214         switch ($algorithm) {
  3215. 3215             case 'rsaEncryption':
  3216. 3216                 if (!class_exists('Crypt_RSA')) {
  3217. 3217                     include_once 'Crypt/RSA.php';
  3218. 3218                 }
  3219. 3219                 $this->publicKey = new Crypt_RSA();
  3220. 3220                 $this->publicKey->loadKey($key);
  3221. 3221                 $this->publicKey->setPublicKey();
  3222. 3222                 break;
  3223. 3223             default:
  3224. 3224                 $this->publicKey null;
  3225. 3225         }
  3226. 3226 
  3227. 3227         $this->currentKeyIdentifier null;
  3228. 3228         $this->currentCert $spkac;
  3229. 3229 
  3230. 3230         return $spkac;
  3231. 3231     }
  3232. 3232 
  3233. 3233     /**
  3234. 3234      * Save a SPKAC CSR request
  3235. 3235      *
  3236. 3236      * @param array $csr
  3237. 3237      * @param int $format optional
  3238. 3238      * @access public
  3239. 3239      * @return string
  3240. 3240      */
  3241. 3241     function saveSPKAC($spkac$format FILE_X509_FORMAT_PEM)
  3242. 3242     {
  3243. 3243         if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) {
  3244. 3244             return false;
  3245. 3245         }
  3246. 3246 
  3247. 3247         $algorithm $this->_subArray($spkac'publicKeyAndChallenge/spki/algorithm/algorithm');
  3248. 3248         switch (true) {
  3249. 3249             case !$algorithm:
  3250. 3250             case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
  3251. 3251                 break;
  3252. 3252             default:
  3253. 3253                 switch ($algorithm) {
  3254. 3254                     case 'rsaEncryption':
  3255. 3255                         $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
  3256. 3256                             = base64_encode("\0" base64_decode(preg_replace('#-.+-|[\r\n]#'''$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])));
  3257. 3257                 }
  3258. 3258         }
  3259. 3259 
  3260. 3260         $asn1 = new File_ASN1();
  3261. 3261 
  3262. 3262         $asn1->loadOIDs($this->oids);
  3263. 3263         $spkac $asn1->encodeDER($spkac$this->SignedPublicKeyAndChallenge);
  3264. 3264 
  3265. 3265         switch ($format) {
  3266. 3266             case FILE_X509_FORMAT_DER:
  3267. 3267                 return $spkac;
  3268. 3268             // case FILE_X509_FORMAT_PEM:
  3269. 3269             default:
  3270. 3270                 // OpenSSL's implementation of SPKAC requires the SPKAC be preceded by SPKAC= and since there are pretty much
  3271. 3271                 // no other SPKAC decoders phpseclib will use that same format
  3272. 3272                 return 'SPKAC=' base64_encode($spkac);
  3273. 3273         }
  3274. 3274     }
  3275. 3275 
  3276. 3276     /**
  3277. 3277      * Load a Certificate Revocation List
  3278. 3278      *
  3279. 3279      * @param string $crl
  3280. 3280      * @access public
  3281. 3281      * @return mixed
  3282. 3282      */
  3283. 3283     function loadCRL($crl$mode FILE_X509_FORMAT_AUTO_DETECT)
  3284. 3284     {
  3285. 3285         if (is_array($crl) && isset($crl['tbsCertList'])) {
  3286. 3286             $this->currentCert $crl;
  3287. 3287             unset($this->signatureSubject);
  3288. 3288             return $crl;
  3289. 3289         }
  3290. 3290 
  3291. 3291         $asn1 = new File_ASN1();
  3292. 3292 
  3293. 3293         if ($mode != FILE_X509_FORMAT_DER) {
  3294. 3294             $newcrl $this->_extractBER($crl);
  3295. 3295             if ($mode == FILE_X509_FORMAT_PEM && $crl == $newcrl) {
  3296. 3296                 return false;
  3297. 3297             }
  3298. 3298             $crl $newcrl;
  3299. 3299         }
  3300. 3300         $orig $crl;
  3301. 3301 
  3302. 3302         if ($crl === false) {
  3303. 3303             $this->currentCert false;
  3304. 3304             return false;
  3305. 3305         }
  3306. 3306 
  3307. 3307         $asn1->loadOIDs($this->oids);
  3308. 3308         $decoded $asn1->decodeBER($crl);
  3309. 3309 
  3310. 3310         if (empty($decoded)) {
  3311. 3311             $this->currentCert false;
  3312. 3312             return false;
  3313. 3313         }
  3314. 3314 
  3315. 3315         $crl $asn1->asn1map($decoded[0], $this->CertificateList);
  3316. 3316         if (!isset($crl) || $crl === false) {
  3317. 3317             $this->currentCert false;
  3318. 3318             return false;
  3319. 3319         }
  3320. 3320 
  3321. 3321         $this->signatureSubject substr($orig$decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
  3322. 3322 
  3323. 3323         $this->_mapInDNs($crl'tbsCertList/issuer/rdnSequence'$asn1);
  3324. 3324         if ($this->_isSubArrayValid($crl'tbsCertList/crlExtensions')) {
  3325. 3325             $this->_mapInExtensions($crl'tbsCertList/crlExtensions'$asn1);
  3326. 3326         }
  3327. 3327         if ($this->_isSubArrayValid($crl'tbsCertList/revokedCertificates')) {
  3328. 3328             $rclist_ref = &$this->_subArrayUnchecked($crl'tbsCertList/revokedCertificates');
  3329. 3329             if ($rclist_ref) {
  3330. 3330                 $rclist $crl['tbsCertList']['revokedCertificates'];
  3331. 3331                 foreach ($rclist as $i => $extension) {
  3332. 3332                     if ($this->_isSubArrayValid($rclist"$i/crlEntryExtensions"$asn1)) {
  3333. 3333                         $this->_mapInExtensions($rclist_ref"$i/crlEntryExtensions"$asn1);
  3334. 3334                     }
  3335. 3335                 }
  3336. 3336             }
  3337. 3337         }
  3338. 3338 
  3339. 3339         $this->currentKeyIdentifier null;
  3340. 3340         $this->currentCert $crl;
  3341. 3341 
  3342. 3342         return $crl;
  3343. 3343     }
  3344. 3344 
  3345. 3345     /**
  3346. 3346      * Save Certificate Revocation List.
  3347. 3347      *
  3348. 3348      * @param array $crl
  3349. 3349      * @param int $format optional
  3350. 3350      * @access public
  3351. 3351      * @return string
  3352. 3352      */
  3353. 3353     function saveCRL($crl$format FILE_X509_FORMAT_PEM)
  3354. 3354     {
  3355. 3355         if (!is_array($crl) || !isset($crl['tbsCertList'])) {
  3356. 3356             return false;
  3357. 3357         }
  3358. 3358 
  3359. 3359         $asn1 = new File_ASN1();
  3360. 3360 
  3361. 3361         $asn1->loadOIDs($this->oids);
  3362. 3362 
  3363. 3363         $filters = array();
  3364. 3364         $filters['tbsCertList']['issuer']['rdnSequence']['value']
  3365. 3365             = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  3366. 3366         $filters['tbsCertList']['signature']['parameters']
  3367. 3367             = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  3368. 3368         $filters['signatureAlgorithm']['parameters']
  3369. 3369             = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
  3370. 3370 
  3371. 3371         if (empty($crl['tbsCertList']['signature']['parameters'])) {
  3372. 3372             $filters['tbsCertList']['signature']['parameters']
  3373. 3373                 = array('type' => FILE_ASN1_TYPE_NULL);
  3374. 3374         }
  3375. 3375 
  3376. 3376         if (empty($crl['signatureAlgorithm']['parameters'])) {
  3377. 3377             $filters['signatureAlgorithm']['parameters']
  3378. 3378                 = array('type' => FILE_ASN1_TYPE_NULL);
  3379. 3379         }
  3380. 3380 
  3381. 3381         $asn1->loadFilters($filters);
  3382. 3382 
  3383. 3383         $this->_mapOutDNs($crl'tbsCertList/issuer/rdnSequence'$asn1);
  3384. 3384         $this->_mapOutExtensions($crl'tbsCertList/crlExtensions'$asn1);
  3385. 3385         $rclist = &$this->_subArray($crl'tbsCertList/revokedCertificates');
  3386. 3386         if (is_array($rclist)) {
  3387. 3387             foreach ($rclist as $i => $extension) {
  3388. 3388                 $this->_mapOutExtensions($rclist"$i/crlEntryExtensions"$asn1);
  3389. 3389             }
  3390. 3390         }
  3391. 3391 
  3392. 3392         $crl $asn1->encodeDER($crl$this->CertificateList);
  3393. 3393 
  3394. 3394         switch ($format) {
  3395. 3395             case FILE_X509_FORMAT_DER:
  3396. 3396                 return $crl;
  3397. 3397             // case FILE_X509_FORMAT_PEM:
  3398. 3398             default:
  3399. 3399                 return "-----BEGIN X509 CRL-----\r\n" chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
  3400. 3400         }
  3401. 3401     }
  3402. 3402 
  3403. 3403     /**
  3404. 3404      * Helper function to build a time field according to RFC 3280 section
  3405. 3405      *  - 4.1.2.5 Validity
  3406. 3406      *  - 5.1.2.4 This Update
  3407. 3407      *  - 5.1.2.5 Next Update
  3408. 3408      *  - 5.1.2.6 Revoked Certificates
  3409. 3409      * by choosing utcTime iff year of date given is before 2050 and generalTime else.
  3410. 3410      *
  3411. 3411      * @param string $date in format date('D, d M Y H:i:s O')
  3412. 3412      * @access private
  3413. 3413      * @return array
  3414. 3414      */
  3415. 3415     function _timeField($date)
  3416. 3416     {
  3417. 3417         if (is_object($date) && strtolower(get_class($date)) == 'file_asn1_element') {
  3418. 3418             return $date;
  3419. 3419         }
  3420. 3420         if (!class_exists('DateTime')) {
  3421. 3421             $year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this
  3422. 3422         } else {
  3423. 3423             $dateObj = new DateTime($date, new DateTimeZone('GMT'));
  3424. 3424             $year $dateObj->format('Y');
  3425. 3425         }
  3426. 3426         if ($year 2050) {
  3427. 3427             return array('utcTime' => $date);
  3428. 3428         } else {
  3429. 3429             return array('generalTime' => $date);
  3430. 3430         }
  3431. 3431     }
  3432. 3432 
  3433. 3433     /**
  3434. 3434      * Sign an X.509 certificate
  3435. 3435      *
  3436. 3436      * $issuer's private key needs to be loaded.
  3437. 3437      * $subject can be either an existing X.509 cert (if you want to resign it),
  3438. 3438      * a CSR or something with the DN and public key explicitly set.
  3439. 3439      *
  3440. 3440      * @param File_X509 $issuer
  3441. 3441      * @param File_X509 $subject
  3442. 3442      * @param string $signatureAlgorithm optional
  3443. 3443      * @access public
  3444. 3444      * @return mixed
  3445. 3445      */
  3446. 3446     function sign($issuer$subject$signatureAlgorithm 'sha1WithRSAEncryption')
  3447. 3447     {
  3448. 3448         if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
  3449. 3449             return false;
  3450. 3450         }
  3451. 3451 
  3452. 3452         if (isset($subject->publicKey) && !($subjectPublicKey $subject->_formatSubjectPublicKey())) {
  3453. 3453             return false;
  3454. 3454         }
  3455. 3455 
  3456. 3456         $currentCert = isset($this->currentCert) ? $this->currentCert null;
  3457. 3457         $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubjectnull;
  3458. 3458 
  3459. 3459         if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
  3460. 3460             $this->currentCert $subject->currentCert;
  3461. 3461             $this->currentCert['tbsCertificate']['signature']['algorithm'] = $signatureAlgorithm;
  3462. 3462             $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
  3463. 3463 
  3464. 3464             if (!empty($this->startDate)) {
  3465. 3465                 $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate);
  3466. 3466             }
  3467. 3467             if (!empty($this->endDate)) {
  3468. 3468                 $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate);
  3469. 3469             }
  3470. 3470             if (!empty($this->serialNumber)) {
  3471. 3471                 $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
  3472. 3472             }
  3473. 3473             if (!empty($subject->dn)) {
  3474. 3474                 $this->currentCert['tbsCertificate']['subject'] = $subject->dn;
  3475. 3475             }
  3476. 3476             if (!empty($subject->publicKey)) {
  3477. 3477                 $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
  3478. 3478             }
  3479. 3479             $this->removeExtension('id-ce-authorityKeyIdentifier');
  3480. 3480             if (isset($subject->domains)) {
  3481. 3481                 $this->removeExtension('id-ce-subjectAltName');
  3482. 3482             }
  3483. 3483         } elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) {
  3484. 3484             return false;
  3485. 3485         } else {
  3486. 3486             if (!isset($subject->publicKey)) {
  3487. 3487                 return false;
  3488. 3488             }
  3489. 3489 
  3490. 3490             if (!class_exists('DateTime')) {
  3491. 3491                 $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
  3492. 3492                 $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O'strtotime('+1 year'));
  3493. 3493             } else {
  3494. 3494                 $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
  3495. 3495                 $startDate = !empty($this->startDate) ? $this->startDate $startDate->format('D, d M Y H:i:s O');
  3496. 3496 
  3497. 3497                 $endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
  3498. 3498                 $endDate = !empty($this->endDate) ? $this->endDate $endDate->format('D, d M Y H:i:s O');
  3499. 3499             }
  3500. 3500             if (!empty($this->serialNumber)) {
  3501. 3501                 $serialNumber $this->serialNumber;
  3502. 3502             } else {
  3503. 3503                 if (!function_exists('crypt_random_string')) {
  3504. 3504                     include_once 'Crypt/Random.php';
  3505. 3505                 }
  3506. 3506                 /* "The serial number MUST be a positive integer"
  3507. 3507                    "Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
  3508. 3508                     -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
  3509. 3509 
  3510. 3510                    for the integer to be positive the leading bit needs to be 0 hence the
  3511. 3511                    application of a bitmap
  3512. 3512                 */
  3513. 3513                 $serialNumber = new Math_BigInteger(crypt_random_string(20) & ("\x7F" str_repeat("\xFF"19)), 256);
  3514. 3514             }
  3515. 3515 
  3516. 3516             $this->currentCert = array(
  3517. 3517                 'tbsCertificate' =>
  3518. 3518                     array(
  3519. 3519                         'version' => 'v3',
  3520. 3520                         'serialNumber' => $serialNumber// $this->setserialNumber()
  3521. 3521                         'signature' => array('algorithm' => $signatureAlgorithm),
  3522. 3522                         'issuer' => false// this is going to be overwritten later
  3523. 3523                         'validity' => array(
  3524. 3524                             'notBefore' => $this->_timeField($startDate), // $this->setStartDate()
  3525. 3525                             'notAfter' => $this->_timeField($endDate)   // $this->setEndDate()
  3526. 3526                         ),
  3527. 3527                         'subject' => $subject->dn,
  3528. 3528                         'subjectPublicKeyInfo' => $subjectPublicKey
  3529. 3529                     ),
  3530. 3530                     'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
  3531. 3531                     'signature'          => false // this is going to be overwritten later
  3532. 3532             );
  3533. 3533 
  3534. 3534             // Copy extensions from CSR.
  3535. 3535             $csrexts $subject->getAttribute('pkcs-9-at-extensionRequest'0);
  3536. 3536 
  3537. 3537             if (!empty($csrexts)) {
  3538. 3538                 $this->currentCert['tbsCertificate']['extensions'] = $csrexts;
  3539. 3539             }
  3540. 3540         }
  3541. 3541 
  3542. 3542         $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;
  3543. 3543 
  3544. 3544         if (isset($issuer->currentKeyIdentifier)) {
  3545. 3545             $this->setExtension('id-ce-authorityKeyIdentifier', array(
  3546. 3546                     //'authorityCertIssuer' => array(
  3547. 3547                     //    array(
  3548. 3548                     //        'directoryName' => $issuer->dn
  3549. 3549                     //    )
  3550. 3550                     //),
  3551. 3551                     'keyIdentifier' => $issuer->currentKeyIdentifier
  3552. 3552                 ));
  3553. 3553             //$extensions = &$this->currentCert['tbsCertificate']['extensions'];
  3554. 3554             //if (isset($issuer->serialNumber)) {
  3555. 3555             //    $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
  3556. 3556             //}
  3557. 3557             //unset($extensions);
  3558. 3558         }
  3559. 3559 
  3560. 3560         if (isset($subject->currentKeyIdentifier)) {
  3561. 3561             $this->setExtension('id-ce-subjectKeyIdentifier'$subject->currentKeyIdentifier);
  3562. 3562         }
  3563. 3563 
  3564. 3564         $altName = array();
  3565. 3565 
  3566. 3566         if (isset($subject->domains) && count($subject->domains)) {
  3567. 3567             $altName array_map(array('File_X509''_dnsName'), $subject->domains);
  3568. 3568         }
  3569. 3569 
  3570. 3570         if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {
  3571. 3571             // should an IP address appear as the CN if no domain name is specified? idk
  3572. 3572             //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1);
  3573. 3573             $ipAddresses = array();
  3574. 3574             foreach ($subject->ipAddresses as $ipAddress) {
  3575. 3575                 $encoded $subject->_ipAddress($ipAddress);
  3576. 3576                 if ($encoded !== false) {
  3577. 3577                     $ipAddresses[] = $encoded;
  3578. 3578                 }
  3579. 3579             }
  3580. 3580             if (count($ipAddresses)) {
  3581. 3581                 $altName array_merge($altName$ipAddresses);
  3582. 3582             }
  3583. 3583         }
  3584. 3584 
  3585. 3585         if (!empty($altName)) {
  3586. 3586             $this->setExtension('id-ce-subjectAltName'$altName);
  3587. 3587         }
  3588. 3588 
  3589. 3589         if ($this->caFlag) {
  3590. 3590             $keyUsage $this->getExtension('id-ce-keyUsage');
  3591. 3591             if (!$keyUsage) {
  3592. 3592                 $keyUsage = array();
  3593. 3593             }
  3594. 3594 
  3595. 3595             $this->setExtension(
  3596. 3596                 'id-ce-keyUsage',
  3597. 3597                 array_values(array_unique(array_merge($keyUsage, array('cRLSign''keyCertSign'))))
  3598. 3598             );
  3599. 3599 
  3600. 3600             $basicConstraints $this->getExtension('id-ce-basicConstraints');
  3601. 3601             if (!$basicConstraints) {
  3602. 3602                 $basicConstraints = array();
  3603. 3603             }
  3604. 3604 
  3605. 3605             $this->setExtension(
  3606. 3606                 'id-ce-basicConstraints',
  3607. 3607                 array_unique(array_merge(array('cA' => true), $basicConstraints)),
  3608. 3608                 true
  3609. 3609             );
  3610. 3610 
  3611. 3611             if (!isset($subject->currentKeyIdentifier)) {
  3612. 3612                 $this->setExtension('id-ce-subjectKeyIdentifier'base64_encode($this->computeKeyIdentifier($this->currentCert)), falsefalse);
  3613. 3613             }
  3614. 3614         }
  3615. 3615 
  3616. 3616         // resync $this->signatureSubject
  3617. 3617         // save $tbsCertificate in case there are any File_ASN1_Element objects in it
  3618. 3618         $tbsCertificate $this->currentCert['tbsCertificate'];
  3619. 3619         $this->loadX509($this->saveX509($this->currentCert));
  3620. 3620 
  3621. 3621         $result $this->_sign($issuer->privateKey$signatureAlgorithm);
  3622. 3622         $result['tbsCertificate'] = $tbsCertificate;
  3623. 3623 
  3624. 3624         $this->currentCert $currentCert;
  3625. 3625         $this->signatureSubject $signatureSubject;
  3626. 3626 
  3627. 3627         return $result;
  3628. 3628     }
  3629. 3629 
  3630. 3630     /**
  3631. 3631      * Sign a CSR
  3632. 3632      *
  3633. 3633      * @access public
  3634. 3634      * @return mixed
  3635. 3635      */
  3636. 3636     function signCSR($signatureAlgorithm 'sha1WithRSAEncryption')
  3637. 3637     {
  3638. 3638         if (!is_object($this->privateKey) || empty($this->dn)) {
  3639. 3639             return false;
  3640. 3640         }
  3641. 3641 
  3642. 3642         $origPublicKey $this->publicKey;
  3643. 3643         $class get_class($this->privateKey);
  3644. 3644         $this->publicKey = new $class();
  3645. 3645         $this->publicKey->loadKey($this->privateKey->getPublicKey());
  3646. 3646         $this->publicKey->setPublicKey();
  3647. 3647         if (!($publicKey $this->_formatSubjectPublicKey())) {
  3648. 3648             return false;
  3649. 3649         }
  3650. 3650         $this->publicKey $origPublicKey;
  3651. 3651 
  3652. 3652         $currentCert = isset($this->currentCert) ? $this->currentCert null;
  3653. 3653         $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubjectnull;
  3654. 3654 
  3655. 3655         if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
  3656. 3656             $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
  3657. 3657             if (!empty($this->dn)) {
  3658. 3658                 $this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
  3659. 3659             }
  3660. 3660             $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
  3661. 3661         } else {
  3662. 3662             $this->currentCert = array(
  3663. 3663                 'certificationRequestInfo' =>
  3664. 3664                     array(
  3665. 3665                         'version' => 'v1',
  3666. 3666                         'subject' => $this->dn,
  3667. 3667                         'subjectPKInfo' => $publicKey
  3668. 3668                     ),
  3669. 3669                     'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
  3670. 3670                     'signature'          => false // this is going to be overwritten later
  3671. 3671             );
  3672. 3672         }
  3673. 3673 
  3674. 3674         // resync $this->signatureSubject
  3675. 3675         // save $certificationRequestInfo in case there are any File_ASN1_Element objects in it
  3676. 3676         $certificationRequestInfo $this->currentCert['certificationRequestInfo'];
  3677. 3677         $this->loadCSR($this->saveCSR($this->currentCert));
  3678. 3678 
  3679. 3679         $result $this->_sign($this->privateKey$signatureAlgorithm);
  3680. 3680         $result['certificationRequestInfo'] = $certificationRequestInfo;
  3681. 3681 
  3682. 3682         $this->currentCert $currentCert;
  3683. 3683         $this->signatureSubject $signatureSubject;
  3684. 3684 
  3685. 3685         return $result;
  3686. 3686     }
  3687. 3687 
  3688. 3688     /**
  3689. 3689      * Sign a SPKAC
  3690. 3690      *
  3691. 3691      * @access public
  3692. 3692      * @return mixed
  3693. 3693      */
  3694. 3694     function signSPKAC($signatureAlgorithm 'sha1WithRSAEncryption')
  3695. 3695     {
  3696. 3696         if (!is_object($this->privateKey)) {
  3697. 3697             return false;
  3698. 3698         }
  3699. 3699 
  3700. 3700         $origPublicKey $this->publicKey;
  3701. 3701         $class get_class($this->privateKey);
  3702. 3702         $this->publicKey = new $class();
  3703. 3703         $this->publicKey->loadKey($this->privateKey->getPublicKey());
  3704. 3704         $this->publicKey->setPublicKey();
  3705. 3705         $publicKey $this->_formatSubjectPublicKey();
  3706. 3706         if (!$publicKey) {
  3707. 3707             return false;
  3708. 3708         }
  3709. 3709         $this->publicKey $origPublicKey;
  3710. 3710 
  3711. 3711         $currentCert = isset($this->currentCert) ? $this->currentCert null;
  3712. 3712         $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubjectnull;
  3713. 3713 
  3714. 3714         // re-signing a SPKAC seems silly but since everything else supports re-signing why not?
  3715. 3715         if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) {
  3716. 3716             $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
  3717. 3717             $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey;
  3718. 3718             if (!empty($this->challenge)) {
  3719. 3719                 // the bitwise AND ensures that the output is a valid IA5String
  3720. 3720                 $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge str_repeat("\x7F"strlen($this->challenge));
  3721. 3721             }
  3722. 3722         } else {
  3723. 3723             $this->currentCert = array(
  3724. 3724                 'publicKeyAndChallenge' =>
  3725. 3725                     array(
  3726. 3726                         'spki' => $publicKey,
  3727. 3727                         // quoting <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>,
  3728. 3728                         // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified."
  3729. 3729                         // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way
  3730. 3730                         // we could alternatively do this instead if we ignored the specs:
  3731. 3731                         // crypt_random_string(8) & str_repeat("\x7F", 8)
  3732. 3732                         'challenge' => !empty($this->challenge) ? $this->challenge ''
  3733. 3733                     ),
  3734. 3734                     'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
  3735. 3735                     'signature'          => false // this is going to be overwritten later
  3736. 3736             );
  3737. 3737         }
  3738. 3738 
  3739. 3739         // resync $this->signatureSubject
  3740. 3740         // save $publicKeyAndChallenge in case there are any File_ASN1_Element objects in it
  3741. 3741         $publicKeyAndChallenge $this->currentCert['publicKeyAndChallenge'];
  3742. 3742         $this->loadSPKAC($this->saveSPKAC($this->currentCert));
  3743. 3743 
  3744. 3744         $result $this->_sign($this->privateKey$signatureAlgorithm);
  3745. 3745         $result['publicKeyAndChallenge'] = $publicKeyAndChallenge;
  3746. 3746 
  3747. 3747         $this->currentCert $currentCert;
  3748. 3748         $this->signatureSubject $signatureSubject;
  3749. 3749 
  3750. 3750         return $result;
  3751. 3751     }
  3752. 3752 
  3753. 3753     /**
  3754. 3754      * Sign a CRL
  3755. 3755      *
  3756. 3756      * $issuer's private key needs to be loaded.
  3757. 3757      *
  3758. 3758      * @param File_X509 $issuer
  3759. 3759      * @param File_X509 $crl
  3760. 3760      * @param string $signatureAlgorithm optional
  3761. 3761      * @access public
  3762. 3762      * @return mixed
  3763. 3763      */
  3764. 3764     function signCRL($issuer$crl$signatureAlgorithm 'sha1WithRSAEncryption')
  3765. 3765     {
  3766. 3766         if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
  3767. 3767             return false;
  3768. 3768         }
  3769. 3769 
  3770. 3770         $currentCert = isset($this->currentCert) ? $this->currentCert null;
  3771. 3771         $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject null;
  3772. 3772         if (!class_exists('DateTime')) {
  3773. 3773             $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
  3774. 3774         } else {
  3775. 3775             $thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
  3776. 3776             $thisUpdate = !empty($this->startDate) ? $this->startDate $thisUpdate->format('D, d M Y H:i:s O');
  3777. 3777         }
  3778. 3778 
  3779. 3779         if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
  3780. 3780             $this->currentCert $crl->currentCert;
  3781. 3781             $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm;
  3782. 3782             $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
  3783. 3783         } else {
  3784. 3784             $this->currentCert = array(
  3785. 3785                 'tbsCertList' =>
  3786. 3786                     array(
  3787. 3787                         'version' => 'v2',
  3788. 3788                         'signature' => array('algorithm' => $signatureAlgorithm),
  3789. 3789                         'issuer' => false// this is going to be overwritten later
  3790. 3790                         'thisUpdate' => $this->_timeField($thisUpdate// $this->setStartDate()
  3791. 3791                     ),
  3792. 3792                     'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
  3793. 3793                     'signature'          => false // this is going to be overwritten later
  3794. 3794             );
  3795. 3795         }
  3796. 3796 
  3797. 3797         $tbsCertList = &$this->currentCert['tbsCertList'];
  3798. 3798         $tbsCertList['issuer'] = $issuer->dn;
  3799. 3799         $tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate);
  3800. 3800 
  3801. 3801         if (!empty($this->endDate)) {
  3802. 3802             $tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); // $this->setEndDate()
  3803. 3803         } else {
  3804. 3804             unset($tbsCertList['nextUpdate']);
  3805. 3805         }
  3806. 3806 
  3807. 3807         if (!empty($this->serialNumber)) {
  3808. 3808             $crlNumber $this->serialNumber;
  3809. 3809         } else {
  3810. 3810             $crlNumber $this->getExtension('id-ce-cRLNumber');
  3811. 3811             // "The CRL number is a non-critical CRL extension that conveys a
  3812. 3812             //  monotonically increasing sequence number for a given CRL scope and
  3813. 3813             //  CRL issuer.  This extension allows users to easily determine when a
  3814. 3814             //  particular CRL supersedes another CRL."
  3815. 3815             // -- https://tools.ietf.org/html/rfc5280#section-5.2.3
  3816. 3816             $crlNumber $crlNumber !== false $crlNumber->add(new Math_BigInteger(1)) : null;
  3817. 3817         }
  3818. 3818 
  3819. 3819         $this->removeExtension('id-ce-authorityKeyIdentifier');
  3820. 3820         $this->removeExtension('id-ce-issuerAltName');
  3821. 3821 
  3822. 3822         // Be sure version >= v2 if some extension found.
  3823. 3823         $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0;
  3824. 3824         if (!$version) {
  3825. 3825             if (!empty($tbsCertList['crlExtensions'])) {
  3826. 3826                 $version 1// v2.
  3827. 3827             } elseif (!empty($tbsCertList['revokedCertificates'])) {
  3828. 3828                 foreach ($tbsCertList['revokedCertificates'] as $cert) {
  3829. 3829                     if (!empty($cert['crlEntryExtensions'])) {
  3830. 3830                         $version 1// v2.
  3831. 3831                     }
  3832. 3832                 }
  3833. 3833             }
  3834. 3834 
  3835. 3835             if ($version) {
  3836. 3836                 $tbsCertList['version'] = $version;
  3837. 3837             }
  3838. 3838         }
  3839. 3839 
  3840. 3840         // Store additional extensions.
  3841. 3841         if (!empty($tbsCertList['version'])) { // At least v2.
  3842. 3842             if (!empty($crlNumber)) {
  3843. 3843                 $this->setExtension('id-ce-cRLNumber'$crlNumber);
  3844. 3844             }
  3845. 3845 
  3846. 3846             if (isset($issuer->currentKeyIdentifier)) {
  3847. 3847                 $this->setExtension('id-ce-authorityKeyIdentifier', array(
  3848. 3848                         //'authorityCertIssuer' => array(
  3849. 3849                         //    array(
  3850. 3850                         //        'directoryName' => $issuer->dn
  3851. 3851                         //    )
  3852. 3852                         //),
  3853. 3853                         'keyIdentifier' => $issuer->currentKeyIdentifier
  3854. 3854                     ));
  3855. 3855                 //$extensions = &$tbsCertList['crlExtensions'];
  3856. 3856                 //if (isset($issuer->serialNumber)) {
  3857. 3857                 //    $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
  3858. 3858                 //}
  3859. 3859                 //unset($extensions);
  3860. 3860             }
  3861. 3861 
  3862. 3862             $issuerAltName $this->getExtension('id-ce-subjectAltName'$issuer->currentCert);
  3863. 3863 
  3864. 3864             if ($issuerAltName !== false) {
  3865. 3865                 $this->setExtension('id-ce-issuerAltName'$issuerAltName);
  3866. 3866             }
  3867. 3867         }
  3868. 3868 
  3869. 3869         if (empty($tbsCertList['revokedCertificates'])) {
  3870. 3870             unset($tbsCertList['revokedCertificates']);
  3871. 3871         }
  3872. 3872 
  3873. 3873         unset($tbsCertList);
  3874. 3874 
  3875. 3875         // resync $this->signatureSubject
  3876. 3876         // save $tbsCertList in case there are any File_ASN1_Element objects in it
  3877. 3877         $tbsCertList $this->currentCert['tbsCertList'];
  3878. 3878         $this->loadCRL($this->saveCRL($this->currentCert));
  3879. 3879 
  3880. 3880         $result $this->_sign($issuer->privateKey$signatureAlgorithm);
  3881. 3881         $result['tbsCertList'] = $tbsCertList;
  3882. 3882 
  3883. 3883         $this->currentCert $currentCert;
  3884. 3884         $this->signatureSubject $signatureSubject;
  3885. 3885 
  3886. 3886         return $result;
  3887. 3887     }
  3888. 3888 
  3889. 3889     /**
  3890. 3890      * X.509 certificate signing helper function.
  3891. 3891      *
  3892. 3892      * @param object $key
  3893. 3893      * @param File_X509 $subject
  3894. 3894      * @param string $signatureAlgorithm
  3895. 3895      * @access public
  3896. 3896      * @return mixed
  3897. 3897      */
  3898. 3898     function _sign($key$signatureAlgorithm)
  3899. 3899     {
  3900. 3900         switch (strtolower(get_class($key))) {
  3901. 3901             case 'crypt_rsa':
  3902. 3902                 switch ($signatureAlgorithm) {
  3903. 3903                     case 'md2WithRSAEncryption':
  3904. 3904                     case 'md5WithRSAEncryption':
  3905. 3905                     case 'sha1WithRSAEncryption':
  3906. 3906                     case 'sha224WithRSAEncryption':
  3907. 3907                     case 'sha256WithRSAEncryption':
  3908. 3908                     case 'sha384WithRSAEncryption':
  3909. 3909                     case 'sha512WithRSAEncryption':
  3910. 3910                         $key->setHash(preg_replace('#WithRSAEncryption$#'''$signatureAlgorithm));
  3911. 3911                         $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
  3912. 3912 
  3913. 3913                         $this->currentCert['signature'] = base64_encode("\0" $key->sign($this->signatureSubject));
  3914. 3914                         return $this->currentCert;
  3915. 3915                 }
  3916. 3916             default:
  3917. 3917                 return false;
  3918. 3918         }
  3919. 3919     }
  3920. 3920 
  3921. 3921     /**
  3922. 3922      * Set certificate start date
  3923. 3923      *
  3924. 3924      * @param string $date
  3925. 3925      * @access public
  3926. 3926      */
  3927. 3927     function setStartDate($date)
  3928. 3928     {
  3929. 3929         if (class_exists('DateTime')) {
  3930. 3930             $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
  3931. 3931             $this->startDate $date->format('D, d M Y H:i:s O');
  3932. 3932         } else {
  3933. 3933             $this->startDate = @date('D, d M Y H:i:s O', @strtotime($date));
  3934. 3934         }
  3935. 3935     }
  3936. 3936 
  3937. 3937     /**
  3938. 3938      * Set certificate end date
  3939. 3939      *
  3940. 3940      * @param string $date
  3941. 3941      * @access public
  3942. 3942      */
  3943. 3943     function setEndDate($date)
  3944. 3944     {
  3945. 3945         /*
  3946. 3946           To indicate that a certificate has no well-defined expiration date,
  3947. 3947           the notAfter SHOULD be assigned the GeneralizedTime value of
  3948. 3948           99991231235959Z.
  3949. 3949 
  3950. 3950           -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
  3951. 3951         */
  3952. 3952         if (strtolower($date) == 'lifetime') {
  3953. 3953             $temp '99991231235959Z';
  3954. 3954             $asn1 = new File_ASN1();
  3955. 3955             $temp chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
  3956. 3956             $this->endDate = new File_ASN1_Element($temp);
  3957. 3957         } else {
  3958. 3958             if (class_exists('DateTime')) {
  3959. 3959                 $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
  3960. 3960                 $this->endDate $date->format('D, d M Y H:i:s O');
  3961. 3961             } else {
  3962. 3962                 $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
  3963. 3963             }
  3964. 3964         }
  3965. 3965     }
  3966. 3966 
  3967. 3967     /**
  3968. 3968      * Set Serial Number
  3969. 3969      *
  3970. 3970      * @param string $serial
  3971. 3971      * @param $base optional
  3972. 3972      * @access public
  3973. 3973      */
  3974. 3974     function setSerialNumber($serial$base = -256)
  3975. 3975     {
  3976. 3976         $this->serialNumber = new Math_BigInteger($serial$base);
  3977. 3977     }
  3978. 3978 
  3979. 3979     /**
  3980. 3980      * Turns the certificate into a certificate authority
  3981. 3981      *
  3982. 3982      * @access public
  3983. 3983      */
  3984. 3984     function makeCA()
  3985. 3985     {
  3986. 3986         $this->caFlag true;
  3987. 3987     }
  3988. 3988 
  3989. 3989     /**
  3990. 3990      * Check for validity of subarray
  3991. 3991      *
  3992. 3992      * This is intended for use in conjunction with _subArrayUnchecked(),
  3993. 3993      * implementing the checks included in _subArray() but without copying
  3994. 3994      * a potentially large array by passing its reference by-value to is_array().
  3995. 3995      *
  3996. 3996      * @param array $root
  3997. 3997      * @param string $path
  3998. 3998      * @return boolean
  3999. 3999      * @access private
  4000. 4000      */
  4001. 4001     function _isSubArrayValid($root$path)
  4002. 4002     {
  4003. 4003         if (!is_array($root)) {
  4004. 4004             return false;
  4005. 4005         }
  4006. 4006 
  4007. 4007         foreach (explode('/'$path) as $i) {
  4008. 4008             if (!is_array($root)) {
  4009. 4009                 return false;
  4010. 4010             }
  4011. 4011 
  4012. 4012             if (!isset($root[$i])) {
  4013. 4013                 return true;
  4014. 4014             }
  4015. 4015 
  4016. 4016             $root $root[$i];
  4017. 4017         }
  4018. 4018 
  4019. 4019         return true;
  4020. 4020     }
  4021. 4021 
  4022. 4022     /**
  4023. 4023      * Get a reference to a subarray
  4024. 4024      *
  4025. 4025      * This variant of _subArray() does no is_array() checking,
  4026. 4026      * so $root should be checked with _isSubArrayValid() first.
  4027. 4027      *
  4028. 4028      * This is here for performance reasons:
  4029. 4029      * Passing a reference (i.e. $root) by-value (i.e. to is_array())
  4030. 4030      * creates a copy. If $root is an especially large array, this is expensive.
  4031. 4031      *
  4032. 4032      * @param array $root
  4033. 4033      * @param string $path  absolute path with / as component separator
  4034. 4034      * @param bool $create optional
  4035. 4035      * @access private
  4036. 4036      * @return array|false
  4037. 4037      */
  4038. 4038     function &_subArrayUnchecked(&$root$path$create false)
  4039. 4039     {
  4040. 4040         $false false;
  4041. 4041 
  4042. 4042         foreach (explode('/'$path) as $i) {
  4043. 4043             if (!isset($root[$i])) {
  4044. 4044                 if (!$create) {
  4045. 4045                     return $false;
  4046. 4046                 }
  4047. 4047 
  4048. 4048                 $root[$i] = array();
  4049. 4049             }
  4050. 4050 
  4051. 4051             $root = &$root[$i];
  4052. 4052         }
  4053. 4053 
  4054. 4054         return $root;
  4055. 4055     }
  4056. 4056 
  4057. 4057     /**
  4058. 4058      * Get a reference to a subarray
  4059. 4059      *
  4060. 4060      * @param array $root
  4061. 4061      * @param string $path  absolute path with / as component separator
  4062. 4062      * @param bool $create optional
  4063. 4063      * @access private
  4064. 4064      * @return array|false
  4065. 4065      */
  4066. 4066     function &_subArray(&$root$path$create false)
  4067. 4067     {
  4068. 4068         $false false;
  4069. 4069 
  4070. 4070         if (!is_array($root)) {
  4071. 4071             return $false;
  4072. 4072         }
  4073. 4073 
  4074. 4074         foreach (explode('/'$path) as $i) {
  4075. 4075             if (!is_array($root)) {
  4076. 4076                 return $false;
  4077. 4077             }
  4078. 4078 
  4079. 4079             if (!isset($root[$i])) {
  4080. 4080                 if (!$create) {
  4081. 4081                     return $false;
  4082. 4082                 }
  4083. 4083 
  4084. 4084                 $root[$i] = array();
  4085. 4085             }
  4086. 4086 
  4087. 4087             $root = &$root[$i];
  4088. 4088         }
  4089. 4089 
  4090. 4090         return $root;
  4091. 4091     }
  4092. 4092 
  4093. 4093     /**
  4094. 4094      * Get a reference to an extension subarray
  4095. 4095      *
  4096. 4096      * @param array $root
  4097. 4097      * @param string $path optional absolute path with / as component separator
  4098. 4098      * @param bool $create optional
  4099. 4099      * @access private
  4100. 4100      * @return array|false
  4101. 4101      */
  4102. 4102     function &_extensions(&$root$path null$create false)
  4103. 4103     {
  4104. 4104         if (!isset($root)) {
  4105. 4105             $root $this->currentCert;
  4106. 4106         }
  4107. 4107 
  4108. 4108         switch (true) {
  4109. 4109             case !empty($path):
  4110. 4110             case !is_array($root):
  4111. 4111                 break;
  4112. 4112             case isset($root['tbsCertificate']):
  4113. 4113                 $path 'tbsCertificate/extensions';
  4114. 4114                 break;
  4115. 4115             case isset($root['tbsCertList']):
  4116. 4116                 $path 'tbsCertList/crlExtensions';
  4117. 4117                 break;
  4118. 4118             case isset($root['certificationRequestInfo']):
  4119. 4119                 $pth 'certificationRequestInfo/attributes';
  4120. 4120                 $attributes = &$this->_subArray($root$pth$create);
  4121. 4121 
  4122. 4122                 if (is_array($attributes)) {
  4123. 4123                     foreach ($attributes as $key => $value) {
  4124. 4124                         if ($value['type'] == 'pkcs-9-at-extensionRequest') {
  4125. 4125                             $path "$pth/$key/value/0";
  4126. 4126                             break 2;
  4127. 4127                         }
  4128. 4128                     }
  4129. 4129                     if ($create) {
  4130. 4130                         $key count($attributes);
  4131. 4131                         $attributes[] = array('type' => 'pkcs-9-at-extensionRequest''value' => array());
  4132. 4132                         $path "$pth/$key/value/0";
  4133. 4133                     }
  4134. 4134                 }
  4135. 4135                 break;
  4136. 4136         }
  4137. 4137 
  4138. 4138         $extensions = &$this->_subArray($root$path$create);
  4139. 4139 
  4140. 4140         if (!is_array($extensions)) {
  4141. 4141             $false false;
  4142. 4142             return $false;
  4143. 4143         }
  4144. 4144 
  4145. 4145         return $extensions;
  4146. 4146     }
  4147. 4147 
  4148. 4148     /**
  4149. 4149      * Remove an Extension
  4150. 4150      *
  4151. 4151      * @param string $id
  4152. 4152      * @param string $path optional
  4153. 4153      * @access private
  4154. 4154      * @return bool
  4155. 4155      */
  4156. 4156     function _removeExtension($id$path null)
  4157. 4157     {
  4158. 4158         $extensions = &$this->_extensions($this->currentCert$path);
  4159. 4159 
  4160. 4160         if (!is_array($extensions)) {
  4161. 4161             return false;
  4162. 4162         }
  4163. 4163 
  4164. 4164         $result false;
  4165. 4165         foreach ($extensions as $key => $value) {
  4166. 4166             if ($value['extnId'] == $id) {
  4167. 4167                 unset($extensions[$key]);
  4168. 4168                 $result true;
  4169. 4169             }
  4170. 4170         }
  4171. 4171 
  4172. 4172         $extensions array_values($extensions);
  4173. 4173         // fix for https://bugs.php.net/75433 affecting PHP 7.2
  4174. 4174         if (!isset($extensions[0])) {
  4175. 4175             $extensions array_splice($extensions00);
  4176. 4176         }
  4177. 4177         return $result;
  4178. 4178     }
  4179. 4179 
  4180. 4180     /**
  4181. 4181      * Get an Extension
  4182. 4182      *
  4183. 4183      * Returns the extension if it exists and false if not
  4184. 4184      *
  4185. 4185      * @param string $id
  4186. 4186      * @param array $cert optional
  4187. 4187      * @param string $path optional
  4188. 4188      * @access private
  4189. 4189      * @return mixed
  4190. 4190      */
  4191. 4191     function _getExtension($id$cert null$path null)
  4192. 4192     {
  4193. 4193         $extensions $this->_extensions($cert$path);
  4194. 4194 
  4195. 4195         if (!is_array($extensions)) {
  4196. 4196             return false;
  4197. 4197         }
  4198. 4198 
  4199. 4199         foreach ($extensions as $key => $value) {
  4200. 4200             if ($value['extnId'] == $id) {
  4201. 4201                 return $value['extnValue'];
  4202. 4202             }
  4203. 4203         }
  4204. 4204 
  4205. 4205         return false;
  4206. 4206     }
  4207. 4207 
  4208. 4208     /**
  4209. 4209      * Returns a list of all extensions in use
  4210. 4210      *
  4211. 4211      * @param array $cert optional
  4212. 4212      * @param string $path optional
  4213. 4213      * @access private
  4214. 4214      * @return array
  4215. 4215      */
  4216. 4216     function _getExtensions($cert null$path null)
  4217. 4217     {
  4218. 4218         $exts $this->_extensions($cert$path);
  4219. 4219         $extensions = array();
  4220. 4220 
  4221. 4221         if (is_array($exts)) {
  4222. 4222             foreach ($exts as $extension) {
  4223. 4223                 $extensions[] = $extension['extnId'];
  4224. 4224             }
  4225. 4225         }
  4226. 4226 
  4227. 4227         return $extensions;
  4228. 4228     }
  4229. 4229 
  4230. 4230     /**
  4231. 4231      * Set an Extension
  4232. 4232      *
  4233. 4233      * @param string $id
  4234. 4234      * @param mixed $value
  4235. 4235      * @param bool $critical optional
  4236. 4236      * @param bool $replace optional
  4237. 4237      * @param string $path optional
  4238. 4238      * @access private
  4239. 4239      * @return bool
  4240. 4240      */
  4241. 4241     function _setExtension($id$value$critical false$replace true$path null)
  4242. 4242     {
  4243. 4243         $extensions = &$this->_extensions($this->currentCert$pathtrue);
  4244. 4244 
  4245. 4245         if (!is_array($extensions)) {
  4246. 4246             return false;
  4247. 4247         }
  4248. 4248 
  4249. 4249         $newext = array('extnId'  => $id'critical' => $critical'extnValue' => $value);
  4250. 4250 
  4251. 4251         foreach ($extensions as $key => $value) {
  4252. 4252             if ($value['extnId'] == $id) {
  4253. 4253                 if (!$replace) {
  4254. 4254                     return false;
  4255. 4255                 }
  4256. 4256 
  4257. 4257                 $extensions[$key] = $newext;
  4258. 4258                 return true;
  4259. 4259             }
  4260. 4260         }
  4261. 4261 
  4262. 4262         $extensions[] = $newext;
  4263. 4263         return true;
  4264. 4264     }
  4265. 4265 
  4266. 4266     /**
  4267. 4267      * Remove a certificate, CSR or CRL Extension
  4268. 4268      *
  4269. 4269      * @param string $id
  4270. 4270      * @access public
  4271. 4271      * @return bool
  4272. 4272      */
  4273. 4273     function removeExtension($id)
  4274. 4274     {
  4275. 4275         return $this->_removeExtension($id);
  4276. 4276     }
  4277. 4277 
  4278. 4278     /**
  4279. 4279      * Get a certificate, CSR or CRL Extension
  4280. 4280      *
  4281. 4281      * Returns the extension if it exists and false if not
  4282. 4282      *
  4283. 4283      * @param string $id
  4284. 4284      * @param array $cert optional
  4285. 4285      * @access public
  4286. 4286      * @return mixed
  4287. 4287      */
  4288. 4288     function getExtension($id$cert null)
  4289. 4289     {
  4290. 4290         return $this->_getExtension($id$cert);
  4291. 4291     }
  4292. 4292 
  4293. 4293     /**
  4294. 4294      * Returns a list of all extensions in use in certificate, CSR or CRL
  4295. 4295      *
  4296. 4296      * @param array $cert optional
  4297. 4297      * @access public
  4298. 4298      * @return array
  4299. 4299      */
  4300. 4300     function getExtensions($cert null)
  4301. 4301     {
  4302. 4302         return $this->_getExtensions($cert);
  4303. 4303     }
  4304. 4304 
  4305. 4305     /**
  4306. 4306      * Set a certificate, CSR or CRL Extension
  4307. 4307      *
  4308. 4308      * @param string $id
  4309. 4309      * @param mixed $value
  4310. 4310      * @param bool $critical optional
  4311. 4311      * @param bool $replace optional
  4312. 4312      * @access public
  4313. 4313      * @return bool
  4314. 4314      */
  4315. 4315     function setExtension($id$value$critical false$replace true)
  4316. 4316     {
  4317. 4317         return $this->_setExtension($id$value$critical$replace);
  4318. 4318     }
  4319. 4319 
  4320. 4320     /**
  4321. 4321      * Remove a CSR attribute.
  4322. 4322      *
  4323. 4323      * @param string $id
  4324. 4324      * @param int $disposition optional
  4325. 4325      * @access public
  4326. 4326      * @return bool
  4327. 4327      */
  4328. 4328     function removeAttribute($id$disposition FILE_X509_ATTR_ALL)
  4329. 4329     {
  4330. 4330         $attributes = &$this->_subArray($this->currentCert'certificationRequestInfo/attributes');
  4331. 4331 
  4332. 4332         if (!is_array($attributes)) {
  4333. 4333             return false;
  4334. 4334         }
  4335. 4335 
  4336. 4336         $result false;
  4337. 4337         foreach ($attributes as $key => $attribute) {
  4338. 4338             if ($attribute['type'] == $id) {
  4339. 4339                 $n count($attribute['value']);
  4340. 4340                 switch (true) {
  4341. 4341                     case $disposition == FILE_X509_ATTR_APPEND:
  4342. 4342                     case $disposition == FILE_X509_ATTR_REPLACE:
  4343. 4343                         return false;
  4344. 4344                     case $disposition >= $n:
  4345. 4345                         $disposition -= $n;
  4346. 4346                         break;
  4347. 4347                     case $disposition == FILE_X509_ATTR_ALL:
  4348. 4348                     case $n == 1:
  4349. 4349                         unset($attributes[$key]);
  4350. 4350                         $result true;
  4351. 4351                         break;
  4352. 4352                     default:
  4353. 4353                         unset($attributes[$key]['value'][$disposition]);
  4354. 4354                         $attributes[$key]['value'] = array_values($attributes[$key]['value']);
  4355. 4355                         $result true;
  4356. 4356                         break;
  4357. 4357                 }
  4358. 4358                 if ($result && $disposition != FILE_X509_ATTR_ALL) {
  4359. 4359                     break;
  4360. 4360                 }
  4361. 4361             }
  4362. 4362         }
  4363. 4363 
  4364. 4364         $attributes array_values($attributes);
  4365. 4365         return $result;
  4366. 4366     }
  4367. 4367 
  4368. 4368     /**
  4369. 4369      * Get a CSR attribute
  4370. 4370      *
  4371. 4371      * Returns the attribute if it exists and false if not
  4372. 4372      *
  4373. 4373      * @param string $id
  4374. 4374      * @param int $disposition optional
  4375. 4375      * @param array $csr optional
  4376. 4376      * @access public
  4377. 4377      * @return mixed
  4378. 4378      */
  4379. 4379     function getAttribute($id$disposition FILE_X509_ATTR_ALL$csr null)
  4380. 4380     {
  4381. 4381         if (empty($csr)) {
  4382. 4382             $csr $this->currentCert;
  4383. 4383         }
  4384. 4384 
  4385. 4385         $attributes $this->_subArray($csr'certificationRequestInfo/attributes');
  4386. 4386 
  4387. 4387         if (!is_array($attributes)) {
  4388. 4388             return false;
  4389. 4389         }
  4390. 4390 
  4391. 4391         foreach ($attributes as $key => $attribute) {
  4392. 4392             if ($attribute['type'] == $id) {
  4393. 4393                 $n count($attribute['value']);
  4394. 4394                 switch (true) {
  4395. 4395                     case $disposition == FILE_X509_ATTR_APPEND:
  4396. 4396                     case $disposition == FILE_X509_ATTR_REPLACE:
  4397. 4397                         return false;
  4398. 4398                     case $disposition == FILE_X509_ATTR_ALL:
  4399. 4399                         return $attribute['value'];
  4400. 4400                     case $disposition >= $n:
  4401. 4401                         $disposition -= $n;
  4402. 4402                         break;
  4403. 4403                     default:
  4404. 4404                         return $attribute['value'][$disposition];
  4405. 4405                 }
  4406. 4406             }
  4407. 4407         }
  4408. 4408 
  4409. 4409         return false;
  4410. 4410     }
  4411. 4411 
  4412. 4412     /**
  4413. 4413      * Returns a list of all CSR attributes in use
  4414. 4414      *
  4415. 4415      * @param array $csr optional
  4416. 4416      * @access public
  4417. 4417      * @return array
  4418. 4418      */
  4419. 4419     function getAttributes($csr null)
  4420. 4420     {
  4421. 4421         if (empty($csr)) {
  4422. 4422             $csr $this->currentCert;
  4423. 4423         }
  4424. 4424 
  4425. 4425         $attributes $this->_subArray($csr'certificationRequestInfo/attributes');
  4426. 4426         $attrs = array();
  4427. 4427 
  4428. 4428         if (is_array($attributes)) {
  4429. 4429             foreach ($attributes as $attribute) {
  4430. 4430                 $attrs[] = $attribute['type'];
  4431. 4431             }
  4432. 4432         }
  4433. 4433 
  4434. 4434         return $attrs;
  4435. 4435     }
  4436. 4436 
  4437. 4437     /**
  4438. 4438      * Set a CSR attribute
  4439. 4439      *
  4440. 4440      * @param string $id
  4441. 4441      * @param mixed $value
  4442. 4442      * @param bool $disposition optional
  4443. 4443      * @access public
  4444. 4444      * @return bool
  4445. 4445      */
  4446. 4446     function setAttribute($id$value$disposition FILE_X509_ATTR_ALL)
  4447. 4447     {
  4448. 4448         $attributes = &$this->_subArray($this->currentCert'certificationRequestInfo/attributes'true);
  4449. 4449 
  4450. 4450         if (!is_array($attributes)) {
  4451. 4451             return false;
  4452. 4452         }
  4453. 4453 
  4454. 4454         switch ($disposition) {
  4455. 4455             case FILE_X509_ATTR_REPLACE:
  4456. 4456                 $disposition FILE_X509_ATTR_APPEND;
  4457. 4457             case FILE_X509_ATTR_ALL:
  4458. 4458                 $this->removeAttribute($id);
  4459. 4459                 break;
  4460. 4460         }
  4461. 4461 
  4462. 4462         foreach ($attributes as $key => $attribute) {
  4463. 4463             if ($attribute['type'] == $id) {
  4464. 4464                 $n count($attribute['value']);
  4465. 4465                 switch (true) {
  4466. 4466                     case $disposition == FILE_X509_ATTR_APPEND:
  4467. 4467                         $last $key;
  4468. 4468                         break;
  4469. 4469                     case $disposition >= $n:
  4470. 4470                         $disposition -= $n;
  4471. 4471                         break;
  4472. 4472                     default:
  4473. 4473                         $attributes[$key]['value'][$disposition] = $value;
  4474. 4474                         return true;
  4475. 4475                 }
  4476. 4476             }
  4477. 4477         }
  4478. 4478 
  4479. 4479         switch (true) {
  4480. 4480             case $disposition >= 0:
  4481. 4481                 return false;
  4482. 4482             case isset($last):
  4483. 4483                 $attributes[$last]['value'][] = $value;
  4484. 4484                 break;
  4485. 4485             default:
  4486. 4486                 $attributes[] = array('type' => $id'value' => $disposition == FILE_X509_ATTR_ALL $value: array($value));
  4487. 4487                 break;
  4488. 4488         }
  4489. 4489 
  4490. 4490         return true;
  4491. 4491     }
  4492. 4492 
  4493. 4493     /**
  4494. 4494      * Sets the subject key identifier
  4495. 4495      *
  4496. 4496      * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
  4497. 4497      *
  4498. 4498      * @param string $value
  4499. 4499      * @access public
  4500. 4500      */
  4501. 4501     function setKeyIdentifier($value)
  4502. 4502     {
  4503. 4503         if (empty($value)) {
  4504. 4504             unset($this->currentKeyIdentifier);
  4505. 4505         } else {
  4506. 4506             $this->currentKeyIdentifier base64_encode($value);
  4507. 4507         }
  4508. 4508     }
  4509. 4509 
  4510. 4510     /**
  4511. 4511      * Compute a public key identifier.
  4512. 4512      *
  4513. 4513      * Although key identifiers may be set to any unique value, this function
  4514. 4514      * computes key identifiers from public key according to the two
  4515. 4515      * recommended methods (4.2.1.2 RFC 3280).
  4516. 4516      * Highly polymorphic: try to accept all possible forms of key:
  4517. 4517      * - Key object
  4518. 4518      * - File_X509 object with public or private key defined
  4519. 4519      * - Certificate or CSR array
  4520. 4520      * - File_ASN1_Element object
  4521. 4521      * - PEM or DER string
  4522. 4522      *
  4523. 4523      * @param mixed $key optional
  4524. 4524      * @param int $method optional
  4525. 4525      * @access public
  4526. 4526      * @return string binary key identifier
  4527. 4527      */
  4528. 4528     function computeKeyIdentifier($key null$method 1)
  4529. 4529     {
  4530. 4530         if (is_null($key)) {
  4531. 4531             $key $this;
  4532. 4532         }
  4533. 4533 
  4534. 4534         switch (true) {
  4535. 4535             case is_string($key):
  4536. 4536                 break;
  4537. 4537             case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
  4538. 4538                 return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method);
  4539. 4539             case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
  4540. 4540                 return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method);
  4541. 4541             case !is_object($key):
  4542. 4542                 return false;
  4543. 4543             case strtolower(get_class($key)) == 'file_asn1_element':
  4544. 4544                 // Assume the element is a bitstring-packed key.
  4545. 4545                 $asn1 = new File_ASN1();
  4546. 4546                 $decoded $asn1->decodeBER($key->element);
  4547. 4547                 if (empty($decoded)) {
  4548. 4548                     return false;
  4549. 4549                 }
  4550. 4550                 $raw $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING));
  4551. 4551                 if (empty($raw)) {
  4552. 4552                     return false;
  4553. 4553                 }
  4554. 4554                 $raw base64_decode($raw);
  4555. 4555                 // If the key is private, compute identifier from its corresponding public key.
  4556. 4556                 if (!class_exists('Crypt_RSA')) {
  4557. 4557                     include_once 'Crypt/RSA.php';
  4558. 4558                 }
  4559. 4559                 $key = new Crypt_RSA();
  4560. 4560                 if (!$key->loadKey($raw)) {
  4561. 4561                     return false;   // Not an unencrypted RSA key.
  4562. 4562                 }
  4563. 4563                 if ($key->getPrivateKey() !== false) {  // If private.
  4564. 4564                     return $this->computeKeyIdentifier($key$method);
  4565. 4565                 }
  4566. 4566                 $key $raw;    // Is a public key.
  4567. 4567                 break;
  4568. 4568             case strtolower(get_class($key)) == 'file_x509':
  4569. 4569                 if (isset($key->publicKey)) {
  4570. 4570                     return $this->computeKeyIdentifier($key->publicKey$method);
  4571. 4571                 }
  4572. 4572                 if (isset($key->privateKey)) {
  4573. 4573                     return $this->computeKeyIdentifier($key->privateKey$method);
  4574. 4574                 }
  4575. 4575                 if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) {
  4576. 4576                     return $this->computeKeyIdentifier($key->currentCert$method);
  4577. 4577                 }
  4578. 4578                 return false;
  4579. 4579             default: // Should be a key object (i.e.: Crypt_RSA).
  4580. 4580                 $key $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
  4581. 4581                 break;
  4582. 4582         }
  4583. 4583 
  4584. 4584         // If in PEM format, convert to binary.
  4585. 4585         $key $this->_extractBER($key);
  4586. 4586 
  4587. 4587         // Now we have the key string: compute its sha-1 sum.
  4588. 4588         if (!class_exists('Crypt_Hash')) {
  4589. 4589             include_once 'Crypt/Hash.php';
  4590. 4590         }
  4591. 4591         $hash = new Crypt_Hash('sha1');
  4592. 4592         $hash $hash->hash($key);
  4593. 4593 
  4594. 4594         if ($method == 2) {
  4595. 4595             $hash substr($hash, -8);
  4596. 4596             $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
  4597. 4597         }
  4598. 4598 
  4599. 4599         return $hash;
  4600. 4600     }
  4601. 4601 
  4602. 4602     /**
  4603. 4603      * Format a public key as appropriate
  4604. 4604      *
  4605. 4605      * @access private
  4606. 4606      * @return array
  4607. 4607      */
  4608. 4608     function _formatSubjectPublicKey()
  4609. 4609     {
  4610. 4610         if (!isset($this->publicKey) || !is_object($this->publicKey)) {
  4611. 4611             return false;
  4612. 4612         }
  4613. 4613 
  4614. 4614         switch (strtolower(get_class($this->publicKey))) {
  4615. 4615             case 'crypt_rsa':
  4616. 4616                 // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason.
  4617. 4617                 // the former is a good example of how to do fuzzing on the public key
  4618. 4618                 //return new File_ASN1_Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey())));
  4619. 4619                 return array(
  4620. 4620                     'algorithm' => array('algorithm' => 'rsaEncryption'),
  4621. 4621                     'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
  4622. 4622                 );
  4623. 4623             default:
  4624. 4624                 return false;
  4625. 4625         }
  4626. 4626     }
  4627. 4627 
  4628. 4628     /**
  4629. 4629      * Set the domain name's which the cert is to be valid for
  4630. 4630      *
  4631. 4631      * @access public
  4632. 4632      * @return array
  4633. 4633      */
  4634. 4634     function setDomain()
  4635. 4635     {
  4636. 4636         $this->domains func_get_args();
  4637. 4637         $this->removeDNProp('id-at-commonName');
  4638. 4638         $this->setDNProp('id-at-commonName'$this->domains[0]);
  4639. 4639     }
  4640. 4640 
  4641. 4641     /**
  4642. 4642      * Set the IP Addresses's which the cert is to be valid for
  4643. 4643      *
  4644. 4644      * @access public
  4645. 4645      * @param string $ipAddress optional
  4646. 4646      */
  4647. 4647     function setIPAddress()
  4648. 4648     {
  4649. 4649         $this->ipAddresses func_get_args();
  4650. 4650         /*
  4651. 4651         if (!isset($this->domains)) {
  4652. 4652             $this->removeDNProp('id-at-commonName');
  4653. 4653             $this->setDNProp('id-at-commonName', $this->ipAddresses[0]);
  4654. 4654         }
  4655. 4655         */
  4656. 4656     }
  4657. 4657 
  4658. 4658     /**
  4659. 4659      * Helper function to build domain array
  4660. 4660      *
  4661. 4661      * @access private
  4662. 4662      * @param string $domain
  4663. 4663      * @return array
  4664. 4664      */
  4665. 4665     function _dnsName($domain)
  4666. 4666     {
  4667. 4667         return array('dNSName' => $domain);
  4668. 4668     }
  4669. 4669 
  4670. 4670     /**
  4671. 4671      * Helper function to build IP Address array
  4672. 4672      *
  4673. 4673      * (IPv6 is not currently supported)
  4674. 4674      *
  4675. 4675      * @access private
  4676. 4676      * @param string $address
  4677. 4677      * @return array
  4678. 4678      */
  4679. 4679     function _iPAddress($address)
  4680. 4680     {
  4681. 4681         return array('iPAddress' => $address);
  4682. 4682     }
  4683. 4683 
  4684. 4684     /**
  4685. 4685      * Get the index of a revoked certificate.
  4686. 4686      *
  4687. 4687      * @param array $rclist
  4688. 4688      * @param string $serial
  4689. 4689      * @param bool $create optional
  4690. 4690      * @access private
  4691. 4691      * @return int|false
  4692. 4692      */
  4693. 4693     function _revokedCertificate(&$rclist$serial$create false)
  4694. 4694     {
  4695. 4695         $serial = new Math_BigInteger($serial);
  4696. 4696 
  4697. 4697         foreach ($rclist as $i => $rc) {
  4698. 4698             if (!($serial->compare($rc['userCertificate']))) {
  4699. 4699                 return $i;
  4700. 4700             }
  4701. 4701         }
  4702. 4702 
  4703. 4703         if (!$create) {
  4704. 4704             return false;
  4705. 4705         }
  4706. 4706 
  4707. 4707         if (!class_exists('DateTime')) {
  4708. 4708             $revocationDate = @date('D, d M Y H:i:s O');
  4709. 4709         } else {
  4710. 4710             $revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
  4711. 4711             $revocationDate $revocationDate->format('D, d M Y H:i:s O');
  4712. 4712         }
  4713. 4713 
  4714. 4714         $i count($rclist);
  4715. 4715         $rclist[] = array('userCertificate' => $serial,
  4716. 4716                           'revocationDate'  => $this->_timeField($revocationDate));
  4717. 4717         return $i;
  4718. 4718     }
  4719. 4719 
  4720. 4720     /**
  4721. 4721      * Revoke a certificate.
  4722. 4722      *
  4723. 4723      * @param string $serial
  4724. 4724      * @param string $date optional
  4725. 4725      * @access public
  4726. 4726      * @return bool
  4727. 4727      */
  4728. 4728     function revoke($serial$date null)
  4729. 4729     {
  4730. 4730         if (isset($this->currentCert['tbsCertList'])) {
  4731. 4731             if (is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'true))) {
  4732. 4732                 if ($this->_revokedCertificate($rclist$serial) === false) { // If not yet revoked
  4733. 4733                     if (($i $this->_revokedCertificate($rclist$serialtrue)) !== false) {
  4734. 4734                         if (!empty($date)) {
  4735. 4735                             $rclist[$i]['revocationDate'] = $this->_timeField($date);
  4736. 4736                         }
  4737. 4737 
  4738. 4738                         return true;
  4739. 4739                     }
  4740. 4740                 }
  4741. 4741             }
  4742. 4742         }
  4743. 4743 
  4744. 4744         return false;
  4745. 4745     }
  4746. 4746 
  4747. 4747     /**
  4748. 4748      * Unrevoke a certificate.
  4749. 4749      *
  4750. 4750      * @param string $serial
  4751. 4751      * @access public
  4752. 4752      * @return bool
  4753. 4753      */
  4754. 4754     function unrevoke($serial)
  4755. 4755     {
  4756. 4756         if (is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'))) {
  4757. 4757             if (($i $this->_revokedCertificate($rclist$serial)) !== false) {
  4758. 4758                 unset($rclist[$i]);
  4759. 4759                 $rclist array_values($rclist);
  4760. 4760                 return true;
  4761. 4761             }
  4762. 4762         }
  4763. 4763 
  4764. 4764         return false;
  4765. 4765     }
  4766. 4766 
  4767. 4767     /**
  4768. 4768      * Get a revoked certificate.
  4769. 4769      *
  4770. 4770      * @param string $serial
  4771. 4771      * @access public
  4772. 4772      * @return mixed
  4773. 4773      */
  4774. 4774     function getRevoked($serial)
  4775. 4775     {
  4776. 4776         if (is_array($rclist $this->_subArray($this->currentCert'tbsCertList/revokedCertificates'))) {
  4777. 4777             if (($i $this->_revokedCertificate($rclist$serial)) !== false) {
  4778. 4778                 return $rclist[$i];
  4779. 4779             }
  4780. 4780         }
  4781. 4781 
  4782. 4782         return false;
  4783. 4783     }
  4784. 4784 
  4785. 4785     /**
  4786. 4786      * List revoked certificates
  4787. 4787      *
  4788. 4788      * @param array $crl optional
  4789. 4789      * @access public
  4790. 4790      * @return array
  4791. 4791      */
  4792. 4792     function listRevoked($crl null)
  4793. 4793     {
  4794. 4794         if (!isset($crl)) {
  4795. 4795             $crl $this->currentCert;
  4796. 4796         }
  4797. 4797 
  4798. 4798         if (!isset($crl['tbsCertList'])) {
  4799. 4799             return false;
  4800. 4800         }
  4801. 4801 
  4802. 4802         $result = array();
  4803. 4803 
  4804. 4804         if (is_array($rclist $this->_subArray($crl'tbsCertList/revokedCertificates'))) {
  4805. 4805             foreach ($rclist as $rc) {
  4806. 4806                 $result[] = $rc['userCertificate']->toString();
  4807. 4807             }
  4808. 4808         }
  4809. 4809 
  4810. 4810         return $result;
  4811. 4811     }
  4812. 4812 
  4813. 4813     /**
  4814. 4814      * Remove a Revoked Certificate Extension
  4815. 4815      *
  4816. 4816      * @param string $serial
  4817. 4817      * @param string $id
  4818. 4818      * @access public
  4819. 4819      * @return bool
  4820. 4820      */
  4821. 4821     function removeRevokedCertificateExtension($serial$id)
  4822. 4822     {
  4823. 4823         if (is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'))) {
  4824. 4824             if (($i $this->_revokedCertificate($rclist$serial)) !== false) {
  4825. 4825                 return $this->_removeExtension($id"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
  4826. 4826             }
  4827. 4827         }
  4828. 4828 
  4829. 4829         return false;
  4830. 4830     }
  4831. 4831 
  4832. 4832     /**
  4833. 4833      * Get a Revoked Certificate Extension
  4834. 4834      *
  4835. 4835      * Returns the extension if it exists and false if not
  4836. 4836      *
  4837. 4837      * @param string $serial
  4838. 4838      * @param string $id
  4839. 4839      * @param array $crl optional
  4840. 4840      * @access public
  4841. 4841      * @return mixed
  4842. 4842      */
  4843. 4843     function getRevokedCertificateExtension($serial$id$crl null)
  4844. 4844     {
  4845. 4845         if (!isset($crl)) {
  4846. 4846             $crl $this->currentCert;
  4847. 4847         }
  4848. 4848 
  4849. 4849         if (is_array($rclist $this->_subArray($crl'tbsCertList/revokedCertificates'))) {
  4850. 4850             if (($i $this->_revokedCertificate($rclist$serial)) !== false) {
  4851. 4851                 return $this->_getExtension($id$crl"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
  4852. 4852             }
  4853. 4853         }
  4854. 4854 
  4855. 4855         return false;
  4856. 4856     }
  4857. 4857 
  4858. 4858     /**
  4859. 4859      * Returns a list of all extensions in use for a given revoked certificate
  4860. 4860      *
  4861. 4861      * @param string $serial
  4862. 4862      * @param array $crl optional
  4863. 4863      * @access public
  4864. 4864      * @return array
  4865. 4865      */
  4866. 4866     function getRevokedCertificateExtensions($serial$crl null)
  4867. 4867     {
  4868. 4868         if (!isset($crl)) {
  4869. 4869             $crl $this->currentCert;
  4870. 4870         }
  4871. 4871 
  4872. 4872         if (is_array($rclist $this->_subArray($crl'tbsCertList/revokedCertificates'))) {
  4873. 4873             if (($i $this->_revokedCertificate($rclist$serial)) !== false) {
  4874. 4874                 return $this->_getExtensions($crl"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
  4875. 4875             }
  4876. 4876         }
  4877. 4877 
  4878. 4878         return false;
  4879. 4879     }
  4880. 4880 
  4881. 4881     /**
  4882. 4882      * Set a Revoked Certificate Extension
  4883. 4883      *
  4884. 4884      * @param string $serial
  4885. 4885      * @param string $id
  4886. 4886      * @param mixed $value
  4887. 4887      * @param bool $critical optional
  4888. 4888      * @param bool $replace optional
  4889. 4889      * @access public
  4890. 4890      * @return bool
  4891. 4891      */
  4892. 4892     function setRevokedCertificateExtension($serial$id$value$critical false$replace true)
  4893. 4893     {
  4894. 4894         if (isset($this->currentCert['tbsCertList'])) {
  4895. 4895             if (is_array($rclist = &$this->_subArray($this->currentCert'tbsCertList/revokedCertificates'true))) {
  4896. 4896                 if (($i $this->_revokedCertificate($rclist$serialtrue)) !== false) {
  4897. 4897                     return $this->_setExtension($id$value$critical$replace"tbsCertList/revokedCertificates/$i/crlEntryExtensions");
  4898. 4898                 }
  4899. 4899             }
  4900. 4900         }
  4901. 4901 
  4902. 4902         return false;
  4903. 4903     }
  4904. 4904 
  4905. 4905     /**
  4906. 4906      * Extract raw BER from Base64 encoding
  4907. 4907      *
  4908. 4908      * @access private
  4909. 4909      * @param string $str
  4910. 4910      * @return string
  4911. 4911      */
  4912. 4912     function _extractBER($str)
  4913. 4913     {
  4914. 4914         /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
  4915. 4915          * above and beyond the ceritificate.
  4916. 4916          * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
  4917. 4917          *
  4918. 4918          * Bag Attributes
  4919. 4919          *     localKeyID: 01 00 00 00
  4920. 4920          * subject=/O=organization/OU=org unit/CN=common name
  4921. 4921          * issuer=/O=organization/CN=common name
  4922. 4922          */
  4923. 4923         $temp preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms'''$str1);
  4924. 4924         // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
  4925. 4925         $temp preg_replace('#-+[^-]+-+#'''$temp);
  4926. 4926         // remove new lines
  4927. 4927         $temp str_replace(array("\r""\n"' '), ''$temp);
  4928. 4928         $temp preg_match('#^[a-zA-Z\d/+]*={0,2}$#'$temp) ? base64_decode($temp) : false;
  4929. 4929         return $temp != false $temp $str;
  4930. 4930     }
  4931. 4931 
  4932. 4932     /**
  4933. 4933      * Returns the OID corresponding to a name
  4934. 4934      *
  4935. 4935      * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if
  4936. 4936      * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version
  4937. 4937      * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able
  4938. 4938      * to work from version to version.
  4939. 4939      *
  4940. 4940      * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that
  4941. 4941      * what's being passed to it already is an OID and return that instead. A few examples.
  4942. 4942      *
  4943. 4943      * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1'
  4944. 4944      * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
  4945. 4945      * getOID('zzz') == 'zzz'
  4946. 4946      *
  4947. 4947      * @access public
  4948. 4948      * @return string
  4949. 4949      */
  4950. 4950     function getOID($name)
  4951. 4951     {
  4952. 4952         static $reverseMap;
  4953. 4953         if (!isset($reverseMap)) {
  4954. 4954             $reverseMap array_flip($this->oids);
  4955. 4955         }
  4956. 4956         return isset($reverseMap[$name]) ? $reverseMap[$name] : $name;
  4957. 4957     }
  4958. 4958 }